@applicaster/zapp-react-native-utils 12.0.0-alpha.4342066143 → 12.0.0-alpha.4354608902

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 (73) hide show
  1. package/analyticsUtils/AnalyticsEvents/index.ts +7 -0
  2. package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
  3. package/analyticsUtils/events.ts +6 -0
  4. package/analyticsUtils/index.tsx +6 -0
  5. package/appUtils/HooksManager/{Hook.js → Hook.ts} +35 -23
  6. package/appUtils/HooksManager/{constants.js → constants.ts} +1 -1
  7. package/appUtils/HooksManager/{index.js → index.ts} +118 -111
  8. package/appUtils/HooksManager/types.ts +116 -0
  9. package/appUtils/RiverFocusManager/index.js +17 -12
  10. package/appUtils/accessibilityManager/const.ts +50 -0
  11. package/appUtils/accessibilityManager/hooks.ts +31 -0
  12. package/appUtils/accessibilityManager/index.ts +150 -0
  13. package/appUtils/accessibilityManager/logger.ts +5 -0
  14. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -3
  15. package/appUtils/focusManager/__tests__/focusManager.test.js +4 -24
  16. package/appUtils/focusManager/__tests__/index.test.ts +2 -3
  17. package/appUtils/focusManager/index.ts +76 -57
  18. package/appUtils/focusManager/treeDataStructure/Tree/index.js +2 -3
  19. package/appUtils/focusManager/treeDataStructure/Utils/index.js +76 -47
  20. package/appUtils/focusManager/utils/index.ts +100 -0
  21. package/appUtils/focusManagerAux/index.ts +76 -0
  22. package/appUtils/focusManagerAux/utils/index.ts +100 -0
  23. package/appUtils/{index.js → index.ts} +2 -0
  24. package/appUtils/keyCodes/{index.js → index.ts} +2 -0
  25. package/appUtils/keyCodes/keys/index.ts +3 -0
  26. package/appUtils/keyCodes/{keys.android.js → keys/keyEventMapper.ts} +0 -19
  27. package/appUtils/keyCodes/keys/keys.android.ts +18 -0
  28. package/appUtils/keyCodes/keys/keys.ios.ts +25 -0
  29. package/appUtils/keyCodes/keys/keys.ts +15 -0
  30. package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +8 -0
  31. package/appUtils/keyCodes/{keysByPlatform.js → keys/keysByPlatform.ts} +11 -1
  32. package/appUtils/platform/const.ts +25 -0
  33. package/appUtils/platform/index.ts +3 -0
  34. package/appUtils/platform/platformUtils.ts +171 -0
  35. package/appUtils/playerManager/conts.ts +1 -0
  36. package/appUtils/playerManager/index.ts +11 -5
  37. package/appUtils/playerManager/nativePlayerManager.ts +12 -0
  38. package/appUtils/playerManager/player.ts +9 -3
  39. package/appUtils/playerManager/playerNative.ts +5 -14
  40. package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -0
  41. package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
  42. package/arrayUtils/index.ts +5 -0
  43. package/booleanUtils/index.ts +4 -4
  44. package/domUtils/{cookieManager.js → cookieManager.ts} +1 -2
  45. package/functionUtils/index.ts +15 -11
  46. package/keyboardState/index.ts +0 -0
  47. package/modalState/index.ts +1 -5
  48. package/navigationUtils/index.ts +62 -5
  49. package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
  50. package/numberUtils/index.ts +5 -18
  51. package/package.json +2 -2
  52. package/pluginUtils/index.ts +9 -0
  53. package/reactHooks/cell-click/__tests__/index.test.js +9 -5
  54. package/reactHooks/cell-click/index.ts +11 -11
  55. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +30 -70
  56. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
  57. package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
  58. package/reactHooks/feed/useFeedRefresh.tsx +9 -7
  59. package/reactHooks/navigation/index.ts +51 -11
  60. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +21 -23
  61. package/reactHooks/screen/index.ts +2 -0
  62. package/reactHooks/screen/useScreenBackgroundColor.ts +23 -0
  63. package/reactHooks/screen/useScreenContext.ts +1 -2
  64. package/reactUtils/index.ts +91 -0
  65. package/appUtils/focusManager/FocusableEvents.ts +0 -8
  66. package/appUtils/focusManager/treeDataStructure/Utils/__tests__/Utils_android.test.js +0 -1094
  67. package/appUtils/focusManager/treeDataStructure/Utils/index.android.js +0 -261
  68. package/appUtils/keyCodes/keys.ios.js +0 -46
  69. /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
  70. /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
  71. /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
  72. /package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +0 -0
  73. /package/domUtils/{logger.js → logger.ts} +0 -0
@@ -0,0 +1,171 @@
1
+ import { BehaviorSubject } from "rxjs";
2
+
3
+ import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
4
+
5
+ import { PLATFORM_KEYS, PLATFORMS } from "./const";
6
+ import { createLogger, utilsLogger } from "../../logger";
7
+
8
+ const { log_debug } = createLogger({
9
+ category: "General",
10
+ subsystem: "PlatformUtils",
11
+ parent: utilsLogger,
12
+ });
13
+
14
+ export const getUserAgent = () =>
15
+ window?.navigator?.userAgent || global?.navigator?.userAgent;
16
+
17
+ /** VIZIO UTILS */
18
+
19
+ /**
20
+ * Checks if we are on the Vizio platform by using the userAgent string
21
+ *
22
+ * Use this method when you need to identify the platform on runtime
23
+ * before the VIZIO Companion Library has loaded
24
+ */
25
+ export const isVizioPlatform = () => {
26
+ const userAgent = getUserAgent();
27
+
28
+ const isVizioAgent =
29
+ userAgent.includes(PLATFORMS.vizio) ||
30
+ userAgent.includes(PLATFORMS.smartcast) ||
31
+ userAgent.includes(PLATFORMS.conjure);
32
+
33
+ return isVizioAgent;
34
+ };
35
+
36
+ /**
37
+ * Checks if we are on the Vizio platform by checking for the global VIZIO object
38
+ * and the VIZIO Companion Library. This method does not use the userAgent string,
39
+ * because it mainly checks whether we have access to apis, which may not be available yet
40
+ */
41
+ export const hasVizioAPIs = () => {
42
+ const hasAPIs =
43
+ typeof window.VIZIO !== "undefined" &&
44
+ window?.applicaster?.vizioLibraryDidLoad;
45
+
46
+ return hasAPIs;
47
+ };
48
+
49
+ /**
50
+ * Manages device based closed captioning state
51
+ * Returns an observable that emits the current state and updates whenever it changes
52
+ */
53
+ export class ClosedCaptioningManager {
54
+ private ccState$ = new BehaviorSubject<boolean>(false);
55
+ private static ccManagerInstance: ClosedCaptioningManager;
56
+
57
+ private constructor() {
58
+ this.initialize();
59
+ }
60
+
61
+ static getInstance(): ClosedCaptioningManager {
62
+ if (!ClosedCaptioningManager.ccManagerInstance) {
63
+ ClosedCaptioningManager.ccManagerInstance = new ClosedCaptioningManager();
64
+ }
65
+
66
+ return ClosedCaptioningManager.ccManagerInstance;
67
+ }
68
+
69
+ async initialize() {
70
+ const existingState = await sessionStorage.getItem(
71
+ PLATFORM_KEYS.deviceClosedCaptioningEnabled
72
+ );
73
+
74
+ log_debug("Initializing closed captioning manager", { existingState });
75
+
76
+ if (existingState !== null) {
77
+ this.ccState$.next(existingState === "true" || existingState === true);
78
+ }
79
+
80
+ if (hasVizioAPIs()) {
81
+ window.VIZIO!.setClosedCaptionHandler((isCCEnabled) => {
82
+ log_debug("CC state changed", { isCCEnabled });
83
+
84
+ sessionStorage.setItem(
85
+ PLATFORM_KEYS.deviceClosedCaptioningEnabled,
86
+ isCCEnabled
87
+ );
88
+
89
+ this.ccState$.next(isCCEnabled);
90
+ });
91
+ }
92
+ }
93
+
94
+ getCurrentState(): boolean {
95
+ return this.ccState$.getValue();
96
+ }
97
+
98
+ getStateAsObservable() {
99
+ return this.ccState$.asObservable();
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Subscribe to closed captioning state changes
105
+ * @returns Observable<boolean>
106
+ * @example
107
+ * const subscription = getClosedCaptionState().subscribe(
108
+ * isEnabled => setClosedCaptionState(isEnabled)
109
+ * );
110
+ * subscription.unsubscribe(), []);
111
+ */
112
+ export const getClosedCaptionState = () => {
113
+ const ccManager = ClosedCaptioningManager.getInstance();
114
+
115
+ return ccManager.getStateAsObservable();
116
+ };
117
+
118
+ /**
119
+ * Manages device based closed captioning state
120
+ * Returns an observable that emits the current state and updates whenever it changes
121
+ */
122
+ export class TTSManager {
123
+ private ttsState$ = new BehaviorSubject<boolean>(false);
124
+ private static ttsManagerInstance: TTSManager;
125
+
126
+ private constructor() {
127
+ this.initialize();
128
+ }
129
+
130
+ static getInstance(): TTSManager {
131
+ if (!TTSManager.ttsManagerInstance) {
132
+ TTSManager.ttsManagerInstance = new TTSManager();
133
+ }
134
+
135
+ return TTSManager.ttsManagerInstance;
136
+ }
137
+
138
+ async initialize() {
139
+ if (!isVizioPlatform()) return;
140
+
141
+ document.addEventListener(
142
+ "VIZIO_TTS_ENABLED",
143
+ () => {
144
+ this.ttsState$.next(true);
145
+ },
146
+ false
147
+ );
148
+
149
+ document.addEventListener(
150
+ "VIZIO_TTS_DISABLED",
151
+ () => {
152
+ this.ttsState$.next(false);
153
+ },
154
+ false
155
+ );
156
+ }
157
+
158
+ getCurrentState(): boolean {
159
+ return this.ttsState$.getValue();
160
+ }
161
+
162
+ getStateAsObservable() {
163
+ return this.ttsState$.asObservable();
164
+ }
165
+
166
+ readText(text: string) {
167
+ if (!window.VIZIO?.Chromevox) return;
168
+
169
+ window.VIZIO.Chromevox.play(text);
170
+ }
171
+ }
@@ -7,6 +7,7 @@ export enum SharedPlayerCallBacksKeys {
7
7
  OnPlayerPause = "onPlayerPause",
8
8
  OnLoad = "onLoad",
9
9
  OnError = "onError",
10
+ OnVideoError = "onVideoError",
10
11
  OnVideoEnd = "onVideoEnd",
11
12
  OnPlaybackRateChange = "onPlaybackRateChange",
12
13
  OnVideoLoad = "onVideoLoad",
@@ -11,6 +11,7 @@ import {
11
11
  } from "@applicaster/quick-brick-core/App/NetworkStatusProvider/utils";
12
12
  import { setUserLanguagePreference } from "./userLanguagePreference";
13
13
  import { PlayerRole } from "./conts";
14
+ import { RNPlayerManager } from "./nativePlayerManager";
14
15
 
15
16
  const { log_error, log_debug, log_warning } = createLogger({
16
17
  category: "General",
@@ -164,6 +165,10 @@ export class PlayerManager {
164
165
  this.addPlayer(id, playerController);
165
166
 
166
167
  if (playerController.playerRole === PlayerRole.Primary) {
168
+ if (playerController.playerPluginId !== "QuickBrickPlayerPlugin") {
169
+ RNPlayerManager?.["QuickBrickPlayerPlugin"]?.stopBackgroundPlayback?.();
170
+ }
171
+
167
172
  playerController.addListener({
168
173
  id: this.getPlayerListenerId(id),
169
174
  listener: {
@@ -361,9 +366,9 @@ export class PlayerManager {
361
366
  // TODO: Remove
362
367
  if (this.castingReceiver) {
363
368
  this.castingReceiver.rewind(deltaTime);
369
+ } else {
370
+ this.getActivePlayer()?.rewind(deltaTime);
364
371
  }
365
-
366
- this.getActivePlayer()?.rewind(deltaTime);
367
372
  }
368
373
 
369
374
  public forward(deltaTime = null) {
@@ -371,9 +376,9 @@ export class PlayerManager {
371
376
 
372
377
  if (this.castingReceiver) {
373
378
  this.castingReceiver.forward(deltaTime);
379
+ } else {
380
+ this.getActivePlayer()?.forward(deltaTime);
374
381
  }
375
-
376
- this.getActivePlayer()?.forward(deltaTime);
377
382
  }
378
383
 
379
384
  public seeking(time) {
@@ -436,7 +441,7 @@ export class PlayerManager {
436
441
  }
437
442
 
438
443
  // Call method on player just in case
439
- return this.getActivePlayer()?.setLanguage(track);
444
+ return this.getActivePlayer()?.selectTrack(track);
440
445
  }
441
446
 
442
447
  public isRegistered() {
@@ -461,6 +466,7 @@ export class PlayerManager {
461
466
  if (this.castingReceiver) {
462
467
  this.castingReceiver.closeNativePlayer();
463
468
  } else {
469
+ this.getActivePlayer().closeNativePlayer();
464
470
  this.invokeHandler("close");
465
471
  }
466
472
  }
@@ -0,0 +1,12 @@
1
+ import { NativeModules } from "react-native";
2
+ import { isApplePlatform } from "../../reactUtils";
3
+
4
+ export const RNPlayerManager = {
5
+ QuickBrickPlayerPlugin: isApplePlatform()
6
+ ? NativeModules.PlayerModule
7
+ : NativeModules.QuickBrickDefaultPlayerModule,
8
+ KalturaPlayerPlugin: isApplePlatform()
9
+ ? NativeModules.KalturaPlayerModule
10
+ : NativeModules.QBKalturaPlayerModule,
11
+ "quick-brick-player-brightcove": NativeModules.BrightcovePlayerModule,
12
+ };
@@ -359,6 +359,13 @@ export class Player {
359
359
  });
360
360
  };
361
361
 
362
+ onVideoError = (error: Error) => {
363
+ this.invokeListenersUpdate({
364
+ callbackName: SharedPlayerCallBacksKeys.OnVideoError,
365
+ event: error,
366
+ });
367
+ };
368
+
362
369
  onError = (error: Error) => {
363
370
  this.invokeListenersUpdate({
364
371
  callbackName: SharedPlayerCallBacksKeys.OnError,
@@ -650,6 +657,7 @@ export class Player {
650
657
  onPlayerSeekComplete: this.onSeekComplete,
651
658
  onVideoEnd: this.onVideoEnd,
652
659
  onError: this.onError,
660
+ onVideoError: this.onVideoError,
653
661
  onLoad: this.onLoad,
654
662
  onPlayerPause: this.onPlayerPause,
655
663
  onPlayerResume: this.onPlayerResume,
@@ -684,9 +692,6 @@ export class Player {
684
692
  play = () => {};
685
693
  pause = () => {};
686
694
  disableBufferAnimation = (): boolean => true;
687
- setLanguage = (
688
- _selected?: QuickBrickPlayer.TextTrack | QuickBrickPlayer.AudioTrack
689
- ) => {};
690
695
 
691
696
  setPlaybackRate = (_rate) => {};
692
697
  startSleepTimer = (_timestamp) => {};
@@ -728,4 +733,5 @@ export class Player {
728
733
 
729
734
  public supportsNativeControls = (): boolean => false;
730
735
  public supportNativeCast = (): boolean => false;
736
+ public destroy = () => {};
731
737
  }
@@ -2,19 +2,10 @@ import * as R from "ramda";
2
2
  import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
3
3
  import { createLogger, utilsLogger } from "../../logger";
4
4
  import { Player } from "./player";
5
- import { findNodeHandle, NativeModules } from "react-native";
6
- import { isApplePlatform } from "../../reactUtils";
5
+ import { findNodeHandle } from "react-native";
7
6
  import { PlayerRole } from "./conts";
8
7
  import { toBooleanWithDefaultFalse } from "../../booleanUtils";
9
-
10
- const RNPlayerManager = {
11
- QuickBrickPlayerPlugin: isApplePlatform()
12
- ? NativeModules.PlayerModule
13
- : NativeModules.QuickBrickDefaultPlayerModule,
14
- KalturaPlayerPlugin: isApplePlatform()
15
- ? NativeModules.KalturaPlayerModule
16
- : NativeModules.QBKalturaPlayerModule,
17
- };
8
+ import { RNPlayerManager } from "./nativePlayerManager";
18
9
 
19
10
  const logger = createLogger({
20
11
  category: "PlayerNativeController",
@@ -111,7 +102,7 @@ export class PlayerNative extends Player {
111
102
  return;
112
103
  }
113
104
 
114
- const newPosition = R.clamp(0, position, this.playerState.seekableDuration);
105
+ const newPosition = R.clamp(0, this.playerState.seekableDuration, position);
115
106
 
116
107
  if (
117
108
  !this.invokeNativeFunction(
@@ -197,10 +188,10 @@ export class PlayerNative extends Player {
197
188
 
198
189
  disableBufferAnimation = (): boolean => false;
199
190
 
200
- setLanguage = (
191
+ selectTrack = (
201
192
  selected: QuickBrickPlayer.TextTrack | QuickBrickPlayer.AudioTrack
202
193
  ) => {
203
- this.currentPlayerComponent()?.["setLanguage"]?.(selected);
194
+ this.currentPlayerComponent()?.["selectTrack"]?.(selected);
204
195
  };
205
196
 
206
197
  setPlaybackRate = (rate: number) => {
@@ -72,6 +72,7 @@ export const usePlayerControllerSetup = ({
72
72
 
73
73
  return () => {
74
74
  playerManager.unregisterPlayer(playerId);
75
+ playerController.destroy?.();
75
76
  };
76
77
  }, [playerId, playerController]);
77
78
 
@@ -0,0 +1,63 @@
1
+ import { isFilledArray } from "..";
2
+
3
+ describe("isFilledArray", () => {
4
+ it("non-empty array is not empty", () => {
5
+ const value = [1, 2, 3];
6
+
7
+ expect(isFilledArray(value)).toBe(true);
8
+ });
9
+
10
+ it("empty array is empty", () => {
11
+ const value = [];
12
+
13
+ expect(isFilledArray(value)).toBe(false);
14
+ });
15
+
16
+ it("number is not array", () => {
17
+ const value = 123;
18
+
19
+ expect(isFilledArray(value)).toBe(false);
20
+ });
21
+
22
+ it("string is not array", () => {
23
+ const value = "vfnjdk";
24
+
25
+ expect(isFilledArray(value)).toBe(false);
26
+ });
27
+
28
+ it("empty string is not array", () => {
29
+ const value = "";
30
+
31
+ expect(isFilledArray(value)).toBe(false);
32
+ });
33
+
34
+ it("NaN is not array", () => {
35
+ const value = "";
36
+
37
+ expect(isFilledArray(value)).toBe(false);
38
+ });
39
+
40
+ it("object is not array", () => {
41
+ const value = { test: 1 };
42
+
43
+ expect(isFilledArray(value)).toBe(false);
44
+ });
45
+
46
+ it("empty object is not array", () => {
47
+ const value = {};
48
+
49
+ expect(isFilledArray(value)).toBe(false);
50
+ });
51
+
52
+ it("undefined is not array", () => {
53
+ const value = undefined;
54
+
55
+ expect(isFilledArray(value)).toBe(false);
56
+ });
57
+
58
+ it("null is not array", () => {
59
+ const value = null;
60
+
61
+ expect(isFilledArray(value)).toBe(false);
62
+ });
63
+ });
@@ -95,3 +95,8 @@ export const makeListOfIndexes = (size: number): number[] =>
95
95
  export const makeListOf = (value: unknown, size: number): number[] => {
96
96
  return Array(size).fill(value);
97
97
  };
98
+
99
+ /** Checks if a value is a non-empty array */
100
+ export function isFilledArray(value: unknown): boolean {
101
+ return R.is(Array, value) && R.length(value) > 0;
102
+ }
@@ -1,7 +1,7 @@
1
- import * as R from "ramda";
1
+ import { is, toLower } from "ramda";
2
2
 
3
3
  export const toBoolean = (value: any): boolean | undefined => {
4
- if (R.is(Boolean, value)) {
4
+ if (is(Boolean, value)) {
5
5
  return value;
6
6
  }
7
7
 
@@ -17,12 +17,12 @@ export const toBooleanWithDefaultTrue = toBooleanWithDefault(true);
17
17
 
18
18
  export const toBooleanWithDefaultFalse = toBooleanWithDefault(false);
19
19
 
20
- export const isTrue = (value: any): boolean => {
20
+ export const isTrue = (value: unknown): boolean => {
21
21
  if (value === true) {
22
22
  return true;
23
23
  }
24
24
 
25
- if (R.is(String, value) && value.toLowerCase() === "true") {
25
+ if (is(String, value) && toLower(value) === "true") {
26
26
  return true;
27
27
  }
28
28
 
@@ -2,7 +2,6 @@ import { domUtilsLogger } from "./logger";
2
2
 
3
3
  /**
4
4
  * Gets an object of cookies and sets them on the DOM.
5
- * @param {Object} cookies selected data key for label.
6
5
  */
7
6
  export function setCookies(cookies): void {
8
7
  const cookieKeys = Object.keys(cookies);
@@ -25,7 +24,7 @@ export function setCookies(cookies): void {
25
24
  /**
26
25
  * Gets cookies set on DOM
27
26
  */
28
- export function getCookies(): Object {
27
+ export function getCookies(): typeof document.cookie {
29
28
  try {
30
29
  if (document) {
31
30
  return document.cookie;
@@ -73,12 +73,12 @@ type SubscriberFn<T> = (
73
73
 
74
74
  export type EventHandlerFn<T> = (event: string, ...args: unknown[]) => T;
75
75
 
76
- type SubscriberObj = Partial<{
77
- on?: SubscriberFn<SubscriberObj>;
78
- invokeHandler: EventHandlerFn<SubscriberObj>;
79
- removeHandler: EventHandlerFn<SubscriberObj>;
80
- handlers: Record<string, SubscriberFn<SubscriberObj>[] | []>;
81
- }>;
76
+ export type SubscriberObj<T extends {} = {}> = {
77
+ on: SubscriberFn<SubscriberObj<T>>;
78
+ invokeHandler: EventHandlerFn<SubscriberObj<T>>;
79
+ removeHandler: EventHandlerFn<SubscriberObj<T>>;
80
+ handlers: Record<string, SubscriberFn<SubscriberObj<T>>[] | []>;
81
+ } & T;
82
82
 
83
83
  export const clearObject = (object: { [key: string]: unknown }) =>
84
84
  Object.keys(object).forEach((key) => delete object[key]);
@@ -88,7 +88,11 @@ export const clearObject = (object: { [key: string]: unknown }) =>
88
88
  * @param {Object} obj to add the subscription feature on
89
89
  * @returns {Object} subscriber object
90
90
  */
91
- export function subscriber(obj: SubscriberObj = {}): SubscriberObj {
91
+ export function subscriber<T extends Partial<SubscriberObj> = {}>(
92
+ _obj: T = {} as T
93
+ ): SubscriberObj {
94
+ const obj: T = _obj;
95
+
92
96
  if (!obj.handlers) {
93
97
  obj.handlers = {};
94
98
  }
@@ -134,7 +138,7 @@ export function subscriber(obj: SubscriberObj = {}): SubscriberObj {
134
138
  | SubscriberFn<SubscriberObj>[]
135
139
  | [];
136
140
 
137
- return obj;
141
+ return obj as SubscriberObj;
138
142
  };
139
143
 
140
144
  /**
@@ -150,16 +154,16 @@ export function subscriber(obj: SubscriberObj = {}): SubscriberObj {
150
154
 
151
155
  R.compose(R.forEach(callHandlerWithDispose), R.propOr([], event))(handlers);
152
156
 
153
- return obj;
157
+ return obj as SubscriberObj;
154
158
  };
155
159
 
156
160
  obj.removeHandler = function (event, handler) {
157
161
  handlers[event] = R.reject(R.equals(handler), handlers[event]);
158
162
 
159
- return obj;
163
+ return obj as SubscriberObj;
160
164
  };
161
165
 
162
- return obj;
166
+ return obj as SubscriberObj;
163
167
  }
164
168
 
165
169
  /**
File without changes
@@ -31,11 +31,7 @@ export const useModalStore = create<ModalStore>((set) => ({
31
31
  set((state) => ({
32
32
  modalState: { ...state.modalState, ...newState },
33
33
  })),
34
- openModal: ({
35
- item,
36
- options = {},
37
- props = { screenData: {} },
38
- }: OpenModalArg) =>
34
+ openModal: ({ item, options = {}, props }: OpenModalArg) =>
39
35
  set({
40
36
  modalState: {
41
37
  visible: true,
@@ -324,7 +324,13 @@ export function getPathAttributes({
324
324
  * @param {object} rivers
325
325
  * @returns {object}
326
326
  */
327
- export function getRiverFromRoute({ route, rivers }) {
327
+ export function getRiverFromRoute({
328
+ route,
329
+ rivers,
330
+ }: {
331
+ route: string;
332
+ rivers: Record<string, ZappRiver>;
333
+ }) {
328
334
  if (!route) {
329
335
  return null;
330
336
  }
@@ -437,7 +443,7 @@ export const runZappHooksForEntry = async (
437
443
 
438
444
  let success;
439
445
  let payload;
440
- const rivers = appStore.get("rivers");
446
+ const rivers: Record<string, ZappRiver> = appStore.get("rivers");
441
447
  const plugins = appStore.get("plugins");
442
448
  const appData = appStore.get("appData");
443
449
 
@@ -492,13 +498,16 @@ export const runZappHooksForEntry = async (
492
498
  payload = _payload;
493
499
  };
494
500
 
495
- HooksManager(hooksOptions)
501
+ const hooksManager = HooksManager(hooksOptions);
502
+
503
+ hooksManager.subscriber
496
504
  .on(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, handleHookPresent)
497
505
  .on(HOOKS_EVENTS.START_BACKGROUND_HOOK, handleBackgroundHook)
498
506
  .on(HOOKS_EVENTS.ERROR, onFinished(false))
499
507
  .on(HOOKS_EVENTS.CANCEL, onFinished(false))
500
- .on(HOOKS_EVENTS.COMPLETE, onFinished(true))
501
- .handleHooks(entry);
508
+ .on(HOOKS_EVENTS.COMPLETE, onFinished(true));
509
+
510
+ hooksManager.handleHooks(entry);
502
511
 
503
512
  // eslint-disable-next-line no-unmodified-loop-condition
504
513
  while (success === undefined) {
@@ -592,3 +601,51 @@ export function isMenuDisabled(
592
601
  export function routeIsPlayerScreen(currentRoute) {
593
602
  return currentRoute?.includes("/playable");
594
603
  }
604
+
605
+ export const getNavBarProps =
606
+ (currentRiver: ZappRiver, pathname: string, title: string) => () => {
607
+ const props = getNavigationPropsV2({
608
+ currentRiver,
609
+ title,
610
+ category: "nav_bar",
611
+ });
612
+
613
+ if (props) {
614
+ return {
615
+ ...props,
616
+ id: pathname,
617
+ pathname: pathname,
618
+ };
619
+ }
620
+
621
+ return null;
622
+ };
623
+
624
+ export const findMenuPlugin = (
625
+ navigations: ZappNavigation[],
626
+ plugins
627
+ ): QuickBrickPlugin => {
628
+ return resolveNavigationPlugin({
629
+ category: "menu",
630
+ navigations,
631
+ plugins,
632
+ }) as QuickBrickPlugin;
633
+ };
634
+
635
+ export const shouldNavBarDisplayMenu = (currentRiver: ZappRiver, plugins) =>
636
+ findMenuPlugin(currentRiver.navigations, plugins)?.identifier ===
637
+ "quick_brick_side_menu" ||
638
+ currentRiver?.navigations.findIndex(
639
+ (nav) => nav.category === "nav_bar" && nav.general?.override_menu_target
640
+ ) > -1;
641
+
642
+ export const getScreenId = (
643
+ screenData: LegacyNavigationScreenData,
644
+ fallbackRiver: ZappRiver
645
+ ) => {
646
+ return screenData?.targetScreen
647
+ ? screenData?.targetScreen?.id ?? fallbackRiver.id
648
+ : "screen_id" in screenData
649
+ ? screenData.screen_id
650
+ : fallbackRiver.id;
651
+ };
@@ -7,12 +7,24 @@ describe("isLikeNumber", () => {
7
7
  expect(isLikeNumber(value)).toBe(true);
8
8
  });
9
9
 
10
+ it("value is a number", () => {
11
+ const value = 123.45;
12
+
13
+ expect(isLikeNumber(value)).toBe(true);
14
+ });
15
+
10
16
  it("value is a string as number", () => {
11
17
  const value = "123";
12
18
 
13
19
  expect(isLikeNumber(value)).toBe(true);
14
20
  });
15
21
 
22
+ it("value is a string as number", () => {
23
+ const value = "123.45";
24
+
25
+ expect(isLikeNumber(value)).toBe(true);
26
+ });
27
+
16
28
  it("value is empty string", () => {
17
29
  const value = "";
18
30
 
@@ -54,4 +66,10 @@ describe("isLikeNumber", () => {
54
66
 
55
67
  expect(isLikeNumber(value)).toBe(false);
56
68
  });
69
+
70
+ it("NaN is not a number", () => {
71
+ const value = NaN;
72
+
73
+ expect(isLikeNumber(value)).toBe(false);
74
+ });
57
75
  });