@applicaster/zapp-react-native-utils 12.0.0-alpha.5035843303 → 12.0.0-alpha.5050333009

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 (138) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +16 -8
  2. package/analyticsUtils/AnalyticsEvents/index.ts +13 -0
  3. package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
  4. package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
  5. package/analyticsUtils/__tests__/analyticsManager.test.js +75 -73
  6. package/analyticsUtils/analyticsPlugins.ts +25 -0
  7. package/analyticsUtils/events.ts +12 -0
  8. package/analyticsUtils/helpers/hooks.ts +1 -1
  9. package/analyticsUtils/index.tsx +12 -0
  10. package/appUtils/HooksManager/{Hook.js → Hook.ts} +35 -23
  11. package/appUtils/HooksManager/{constants.js → constants.ts} +1 -1
  12. package/appUtils/HooksManager/{index.js → index.ts} +121 -112
  13. package/appUtils/HooksManager/types.ts +116 -0
  14. package/appUtils/RiverFocusManager/index.js +17 -12
  15. package/appUtils/accessibilityManager/const.ts +50 -0
  16. package/appUtils/accessibilityManager/hooks.ts +31 -0
  17. package/appUtils/accessibilityManager/index.ts +150 -0
  18. package/appUtils/accessibilityManager/logger.ts +5 -0
  19. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -3
  20. package/appUtils/focusManager/__tests__/focusManager.test.js +4 -24
  21. package/appUtils/focusManager/__tests__/index.test.ts +2 -3
  22. package/appUtils/focusManager/index.ts +76 -57
  23. package/appUtils/focusManager/treeDataStructure/Tree/index.js +2 -3
  24. package/appUtils/focusManager/treeDataStructure/Utils/index.js +76 -38
  25. package/appUtils/focusManager/utils/index.ts +100 -0
  26. package/appUtils/focusManagerAux/index.ts +76 -0
  27. package/appUtils/focusManagerAux/utils/index.ts +100 -0
  28. package/appUtils/{index.js → index.ts} +2 -0
  29. package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +1 -1
  30. package/appUtils/keyCodes/__tests__/keyCodes.test.js +80 -85
  31. package/appUtils/keyCodes/{index.js → index.ts} +2 -0
  32. package/appUtils/keyCodes/keys/index.ts +3 -0
  33. package/appUtils/keyCodes/{keys.android.js → keys/keyEventMapper.ts} +0 -19
  34. package/appUtils/keyCodes/keys/keys.android.ts +18 -0
  35. package/appUtils/keyCodes/keys/keys.ios.ts +25 -0
  36. package/appUtils/keyCodes/keys/keys.ts +15 -0
  37. package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +9 -0
  38. package/appUtils/keyCodes/{keysByPlatform.js → keys/keysByPlatform.ts} +11 -1
  39. package/appUtils/orientationHelper.ts +1 -1
  40. package/appUtils/platform/const.ts +25 -0
  41. package/appUtils/platform/index.ts +3 -0
  42. package/appUtils/platform/platformUtils.ts +171 -0
  43. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +53 -0
  44. package/appUtils/playerManager/conts.ts +5 -0
  45. package/appUtils/playerManager/index.ts +11 -5
  46. package/appUtils/playerManager/nativePlayerManager.ts +12 -0
  47. package/appUtils/playerManager/player.ts +48 -7
  48. package/appUtils/playerManager/playerFactory.ts +15 -1
  49. package/appUtils/playerManager/playerNative.ts +40 -14
  50. package/appUtils/playerManager/usePlayerControllerSetup.tsx +7 -1
  51. package/appUtils/playerManager/usePlayerState.tsx +13 -1
  52. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +50 -0
  53. package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
  54. package/arrayUtils/index.ts +5 -0
  55. package/booleanUtils/index.ts +4 -4
  56. package/cellUtils/__tests__/cellUtils.test.ts +48 -0
  57. package/cellUtils/__tests__/fixtures.ts +13 -0
  58. package/cellUtils/index.ts +40 -2
  59. package/dateUtils/index.ts +11 -0
  60. package/domUtils/{cookieManager.js → cookieManager.ts} +1 -2
  61. package/focusManager/index.ts +1 -1
  62. package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +2 -2
  63. package/functionUtils/__tests__/functionUtils.test.js +244 -248
  64. package/functionUtils/index.ts +15 -11
  65. package/headersUtils/index.ts +1 -0
  66. package/inflatedUrlUtils/index.ts +4 -4
  67. package/keyboardState/index.ts +0 -0
  68. package/localizationUtils/__tests__/localizationUtils.test.ts +78 -55
  69. package/localizationUtils/index.ts +13 -19
  70. package/logger/index.ts +1 -1
  71. package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
  72. package/manifestUtils/defaultManifestConfigurations/player.js +10 -1
  73. package/manifestUtils/index.js +5 -1
  74. package/manifestUtils/secondaryImage/index.js +18 -15
  75. package/modalState/index.ts +53 -0
  76. package/navigationUtils/index.ts +118 -12
  77. package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
  78. package/numberUtils/index.ts +9 -18
  79. package/package.json +3 -2
  80. package/playerUtils/configurationGenerator.ts +11 -1
  81. package/pluginUtils/index.ts +20 -7
  82. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +2 -0
  83. package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +18 -1
  84. package/reactHooks/cell-click/__tests__/index.test.js +16 -3
  85. package/reactHooks/cell-click/index.ts +14 -14
  86. package/reactHooks/device/index.ts +0 -1
  87. package/reactHooks/device/useIsTablet.tsx +28 -3
  88. package/reactHooks/device/useMemoizedIsTablet.ts +1 -1
  89. package/reactHooks/device/useOrientation.tsx +3 -1
  90. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +30 -70
  91. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
  92. package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
  93. package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
  94. package/reactHooks/feed/useFeedRefresh.tsx +9 -7
  95. package/reactHooks/feed/useInflatedUrl.ts +44 -15
  96. package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -9
  97. package/reactHooks/index.ts +2 -0
  98. package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +66 -59
  99. package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
  100. package/reactHooks/layout/index.ts +2 -1
  101. package/reactHooks/layout/types/index.ts +2 -0
  102. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +11 -16
  103. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +12 -14
  104. package/reactHooks/layout/useDimensions/useDimensions.ts +13 -7
  105. package/reactHooks/navigation/__mocks__/index.ts +15 -0
  106. package/reactHooks/navigation/__mocks__/useNavigation.ts +7 -0
  107. package/reactHooks/navigation/__tests__/index.test.tsx +240 -81
  108. package/reactHooks/navigation/index.ts +246 -84
  109. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +3 -1
  110. package/reactHooks/screen/__mocks__/useScreenContext.ts +3 -1
  111. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +21 -23
  112. package/reactHooks/screen/index.ts +4 -0
  113. package/reactHooks/screen/useScreenBackgroundColor.ts +23 -0
  114. package/reactHooks/screen/useScreenContext.ts +4 -4
  115. package/reactHooks/screen/useScreenData.ts +1 -1
  116. package/reactHooks/screen/useTargetScreenData.ts +7 -0
  117. package/reactHooks/state/useRivers.ts +3 -1
  118. package/reactHooks/useListenEventBusEvent.ts +6 -15
  119. package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +10 -9
  120. package/reactUtils/index.ts +91 -0
  121. package/riverComponetsMeasurementProvider/index.tsx +2 -2
  122. package/stringUtils/index.ts +5 -0
  123. package/testUtils/getByTestId.ts +14 -0
  124. package/testUtils/index.tsx +38 -23
  125. package/types.ts +4 -0
  126. package/zappFrameworkUtils/loginPluginUtils.ts +12 -2
  127. package/appUtils/focusManager/FocusableEvents.ts +0 -8
  128. package/appUtils/focusManager/treeDataStructure/Utils/__tests__/Utils_android.test.js +0 -1094
  129. package/appUtils/focusManager/treeDataStructure/Utils/index.android.js +0 -261
  130. package/appUtils/keyCodes/keys.ios.js +0 -46
  131. package/reactHooks/device/useIsTablet.android.tsx +0 -14
  132. package/reactHooks/device/useIsTablet.ios.tsx +0 -7
  133. package/reactHooks/device/useIsTablet.web.tsx +0 -3
  134. /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
  135. /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
  136. /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
  137. /package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +0 -0
  138. /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
+ }
@@ -41,6 +41,11 @@ export type ChapterMarkerEvent = {
41
41
  actions: ActionChapter[];
42
42
  };
43
43
 
44
+ export type TitleSummaryEvent = {
45
+ title: string | number;
46
+ summary: string | number;
47
+ };
48
+
44
49
  type ChapterMarkersObserverProps = {
45
50
  player: Player;
46
51
  };
@@ -60,6 +65,11 @@ export type PlayNextState = PlayNextConfig & {
60
65
  export class OverlaysObserver {
61
66
  readonly chapterSubject: BehaviorSubject<ChapterMarkerEvent>;
62
67
  private playNextSubject: BehaviorSubject<PlayNextState>;
68
+ private titleSummarySubject: BehaviorSubject<TitleSummaryEvent>;
69
+ private feedUrl: string;
70
+ private reloadData: () => void;
71
+ private updateTitleAndDescription: (data: any) => void;
72
+ private feedDataInterval: any;
63
73
  private releasePlayerObserver?: () => void;
64
74
  readonly entry: ZappEntry;
65
75
  private chapterMarkerEvents: ChapterMarkerEvent[];
@@ -70,14 +80,55 @@ export class OverlaysObserver {
70
80
  constructor({ player }: ChapterMarkersObserverProps) {
71
81
  this.chapterSubject = new BehaviorSubject(null);
72
82
  this.playNextSubject = new BehaviorSubject(null);
83
+
84
+ this.titleSummarySubject = new BehaviorSubject<TitleSummaryEvent>({
85
+ title: player.getEntry()?.title || "",
86
+ summary: player.getEntry()?.summary || "",
87
+ });
88
+
73
89
  this.entry = player.getEntry();
74
90
  this.player = player;
75
91
 
76
92
  this.chapterMarkerEvents = this.prepareChapterMarkers();
77
93
  this.releasePlayerObserver = this.subscribeToPlayerEvents();
94
+ this.feedUrl = "";
95
+ this.reloadData = () => {};
96
+ this.updateTitleAndDescription = () => {};
97
+ this.feedDataInterval = null;
78
98
  void this.preparePlayNext();
79
99
  }
80
100
 
101
+ private setupFeedDataInterval(interval: number) {
102
+ if (this.feedUrl && this.reloadData && this.updateTitleAndDescription) {
103
+ this.feedDataInterval = setInterval(() => {
104
+ this.reloadData();
105
+ }, interval * 1000);
106
+ }
107
+ }
108
+
109
+ public clearFeedDataInterval() {
110
+ if (this.feedDataInterval) {
111
+ clearInterval(this.feedDataInterval);
112
+ this.feedDataInterval = null;
113
+ }
114
+ }
115
+
116
+ public setFeedDataHandlers(
117
+ feedUrl: string,
118
+ reloadData: () => void,
119
+ interval: number,
120
+ updateTitleAndDescription: (data: any) => void
121
+ ) {
122
+ this.feedUrl = feedUrl;
123
+ this.reloadData = reloadData;
124
+ this.updateTitleAndDescription = updateTitleAndDescription;
125
+ this.setupFeedDataInterval(interval);
126
+ }
127
+
128
+ public getTitleSummaryObservable(): Observable<TitleSummaryEvent> {
129
+ return this.titleSummarySubject.asObservable().pipe(distinctUntilChanged());
130
+ }
131
+
81
132
  handleUserCancelPlayNext = () => {
82
133
  this.isCanceledByUser = true;
83
134
  this.playNextSubject.next(null);
@@ -281,7 +332,9 @@ export class OverlaysObserver {
281
332
  onPlayerClose = () => {
282
333
  this.chapterSubject.complete();
283
334
  this.playNextSubject.complete();
335
+ this.titleSummarySubject.complete();
284
336
  this.releasePlayerObserver?.();
337
+ this.clearFeedDataInterval();
285
338
  this.releasePlayerObserver = null;
286
339
  };
287
340
 
@@ -21,6 +21,11 @@ export enum SharedPlayerCallBacksKeys {
21
21
  OnPlayerMute = "onPlayerMute",
22
22
  OnPlayerUnmute = "onPlayerUnmute",
23
23
 
24
+ onVideoFullscreenPlayerDidDismiss = "onVideoFullscreenPlayerDidDismiss",
25
+ onVideoFullscreenPlayerDidPresent = "onVideoFullscreenPlayerDidPresent",
26
+ onVideoFullscreenPlayerWillDismiss = "onVideoFullscreenPlayerWillDismiss",
27
+ onVideoFullscreenPlayerWillPresent = "onVideoFullscreenPlayerWillPresent",
28
+
24
29
  OnAdBegin = "onAdBegin",
25
30
  OnAdBreakBegin = "onAdBreakBegin",
26
31
  OnAdBreakEnd = "onAdBreakEnd",
@@ -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
+ };
@@ -42,7 +42,7 @@ export class Player {
42
42
 
43
43
  protected startPosition?: number | null;
44
44
  readonly playerRole: PlayerRole = PlayerRole.Unspecified;
45
- private chaptersObserver?: OverlaysObserver | null;
45
+ private overlaysObserver?: OverlaysObserver | null;
46
46
  private entryObservable?: BehaviorSubject<ZappEntry> | null;
47
47
 
48
48
  protected listeners: {
@@ -95,11 +95,11 @@ export class Player {
95
95
  }
96
96
 
97
97
  getOverlayObservable = () => {
98
- if (!this.chaptersObserver) {
99
- this.chaptersObserver = new OverlaysObserver({ player: this });
98
+ if (!this.overlaysObserver) {
99
+ this.overlaysObserver = new OverlaysObserver({ player: this });
100
100
  }
101
101
 
102
- return this.chaptersObserver;
102
+ return this.overlaysObserver;
103
103
  };
104
104
 
105
105
  getEntryObservable = (): BehaviorSubject<ZappEntry | null> => {
@@ -278,6 +278,10 @@ export class Player {
278
278
  return this.playerState.isBuffering;
279
279
  };
280
280
 
281
+ isReadyToPlay = (): boolean => {
282
+ return this.playerState.isReadyToPlay;
283
+ };
284
+
281
285
  // Methods to call actions on the player
282
286
  seekTo = (_position: number) => {};
283
287
 
@@ -417,6 +421,32 @@ export class Player {
417
421
  });
418
422
  };
419
423
 
424
+ onVideoFullscreenPlayerWillDismiss = () => {
425
+ this.invokeListenersUpdate({
426
+ callbackName:
427
+ SharedPlayerCallBacksKeys.onVideoFullscreenPlayerWillDismiss,
428
+ });
429
+ };
430
+
431
+ onVideoFullscreenPlayerDidDismiss = () => {
432
+ this.invokeListenersUpdate({
433
+ callbackName: SharedPlayerCallBacksKeys.onVideoFullscreenPlayerDidDismiss,
434
+ });
435
+ };
436
+
437
+ onVideoFullscreenPlayerWillPresent = () => {
438
+ this.invokeListenersUpdate({
439
+ callbackName:
440
+ SharedPlayerCallBacksKeys.onVideoFullscreenPlayerWillPresent,
441
+ });
442
+ };
443
+
444
+ onVideoFullscreenPlayerDidPresent = () => {
445
+ this.invokeListenersUpdate({
446
+ callbackName: SharedPlayerCallBacksKeys.onVideoFullscreenPlayerDidPresent,
447
+ });
448
+ };
449
+
420
450
  public invokeListenersUpdate = ({
421
451
  callbackName,
422
452
  event = {},
@@ -638,6 +668,12 @@ export class Player {
638
668
  onBufferStart: this.onBufferStart,
639
669
  onBufferComplete: this.onBufferComplete,
640
670
 
671
+ onPlayerClose: this.onPlayerClose,
672
+ onVideoFullscreenPlayerWillPresent: this.onVideoFullscreenPlayerWillPresent,
673
+ onVideoFullscreenPlayerDidPresent: this.onVideoFullscreenPlayerDidPresent,
674
+ onVideoFullscreenPlayerWillDismiss: this.onVideoFullscreenPlayerWillDismiss,
675
+ onVideoFullscreenPlayerDidDismiss: this.onVideoFullscreenPlayerDidDismiss,
676
+
641
677
  // Ads Callbacks:
642
678
  onAdBegin: this.onAdBegin,
643
679
  onAdBreakBegin: this.onAdBreakBegin,
@@ -656,9 +692,10 @@ export class Player {
656
692
  play = () => {};
657
693
  pause = () => {};
658
694
  disableBufferAnimation = (): boolean => true;
659
- setLanguage = (
660
- _selected?: QuickBrickPlayer.TextTrack | QuickBrickPlayer.AudioTrack
661
- ) => {};
695
+
696
+ setPlaybackRate = (_rate) => {};
697
+ startSleepTimer = (_timestamp) => {};
698
+ cancelSleepTimer = () => {};
662
699
 
663
700
  getPluginConfiguration = () => null;
664
701
  appStateChange = (_appState, _previousAppState) => {};
@@ -693,4 +730,8 @@ export class Player {
693
730
  isFullScreenSupported(): boolean {
694
731
  return true;
695
732
  }
733
+
734
+ public supportsNativeControls = (): boolean => false;
735
+ public supportNativeCast = (): boolean => false;
736
+ public destroy = () => {};
696
737
  }
@@ -1,17 +1,31 @@
1
1
  import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
2
2
  import { findPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
3
3
  import { Player } from "./player";
4
+ import { PlayerRole } from "./conts";
4
5
 
5
6
  type PlayerFactoryItem = {
6
7
  Component: any;
7
8
  controller: Player;
8
9
  };
9
10
 
11
+ type PlayerFactoryProps = {
12
+ player: any;
13
+ playerId: string;
14
+ autoplay: boolean;
15
+ entry: ZappEntry;
16
+ muted: boolean;
17
+ playerPluginId: string;
18
+ screenConfig: Record<string, any>;
19
+ playerRole: PlayerRole;
20
+ };
21
+
10
22
  interface PlayerConstructor {
11
23
  new (params: any): Player;
12
24
  }
13
25
 
14
- export const playerFactory = (config: any): PlayerFactoryItem | null => {
26
+ export const playerFactory = (
27
+ config: PlayerFactoryProps
28
+ ): PlayerFactoryItem | null => {
15
29
  const IDENTIFIER = config?.playerPluginId;
16
30
 
17
31
  if (!IDENTIFIER) {
@@ -2,18 +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
-
9
- const RNPlayerManager = {
10
- QuickBrickPlayerPlugin: isApplePlatform()
11
- ? NativeModules.PlayerModule
12
- : NativeModules.QuickBrickDefaultPlayerModule,
13
- KalturaPlayerPlugin: isApplePlatform()
14
- ? NativeModules.KalturaPlayerModule
15
- : NativeModules.QBKalturaPlayerModule,
16
- };
7
+ import { toBooleanWithDefaultFalse } from "../../booleanUtils";
8
+ import { RNPlayerManager } from "./nativePlayerManager";
17
9
 
18
10
  const logger = createLogger({
19
11
  category: "PlayerNativeController",
@@ -41,6 +33,9 @@ const PlayerModuleFuncNames = {
41
33
  rewind: "rewind",
42
34
  play: "play",
43
35
  pause: "pause",
36
+ startSleepTimer: "startSleepTimer",
37
+ cancelSleepTimer: "cancelSleepTimer",
38
+ getSleepTimerEnd: "getSleepTimerEnd",
44
39
  };
45
40
 
46
41
  export class PlayerNative extends Player {
@@ -107,7 +102,7 @@ export class PlayerNative extends Player {
107
102
  return;
108
103
  }
109
104
 
110
- const newPosition = R.clamp(0, position, this.playerState.seekableDuration);
105
+ const newPosition = R.clamp(0, this.playerState.seekableDuration, position);
111
106
 
112
107
  if (
113
108
  !this.invokeNativeFunction(
@@ -172,12 +167,35 @@ export class PlayerNative extends Player {
172
167
  }
173
168
  };
174
169
 
170
+ startSleepTimer = (sleepTimestamp: number) => {
171
+ if (
172
+ !this.invokeNativeFunction(
173
+ PlayerModuleFuncNames.startSleepTimer,
174
+ sleepTimestamp
175
+ )
176
+ ) {
177
+ this.currentPlayerComponent()?.["startSleepTimer"](sleepTimestamp);
178
+ }
179
+ };
180
+
181
+ cancelSleepTimer = () => {
182
+ if (!this.invokeNativeFunction(PlayerModuleFuncNames.cancelSleepTimer)) {
183
+ this.currentPlayerComponent()?.[
184
+ PlayerModuleFuncNames.cancelSleepTimer
185
+ ]?.();
186
+ }
187
+ };
188
+
175
189
  disableBufferAnimation = (): boolean => false;
176
190
 
177
- setLanguage = (
191
+ selectTrack = (
178
192
  selected: QuickBrickPlayer.TextTrack | QuickBrickPlayer.AudioTrack
179
193
  ) => {
180
- this.currentPlayerComponent()?.["setLanguage"]?.(selected);
194
+ this.currentPlayerComponent()?.["selectTrack"]?.(selected);
195
+ };
196
+
197
+ setPlaybackRate = (rate: number) => {
198
+ this.currentPlayerComponent()?.["setPlaybackRate"]?.(rate);
181
199
  };
182
200
 
183
201
  getPluginConfiguration = () => {
@@ -265,4 +283,12 @@ export class PlayerNative extends Player {
265
283
 
266
284
  return !(isFullScreenAudioPlayer && this.isAudioItem());
267
285
  };
286
+
287
+ supportsNativeControls = (): boolean =>
288
+ toBooleanWithDefaultFalse(this.getConfig()?.["supports_native_controls"]);
289
+
290
+ supportNativeCast = (): boolean =>
291
+ toBooleanWithDefaultFalse(
292
+ this.playerPluginId === "quick-brick-player-brightcove"
293
+ );
268
294
  }
@@ -4,6 +4,7 @@ import { useEffect } from "react";
4
4
  import { playerManager } from "./index";
5
5
  import { useValidatePlayerConfig } from "../../playerUtils/useValidatePlayerConfig";
6
6
  import { PlayerRole } from "./conts";
7
+ import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
7
8
 
8
9
  // TODO: Rename to ControllerType
9
10
  export const usePlayerControllerSetup = ({
@@ -34,10 +35,14 @@ export const usePlayerControllerSetup = ({
34
35
  useValidatePlayerConfig(config);
35
36
 
36
37
  useEffect(() => {
38
+ const newConfig = isAppleTV()
39
+ ? { supports_native_controls: true, ...config }
40
+ : config;
41
+
37
42
  const newPlayerController = new ControllerType({
38
43
  player: ref,
39
44
  listener: props as any,
40
- config,
45
+ config: newConfig,
41
46
  playerId,
42
47
  entry,
43
48
  playerPluginId: pluginId,
@@ -67,6 +72,7 @@ export const usePlayerControllerSetup = ({
67
72
 
68
73
  return () => {
69
74
  playerManager.unregisterPlayer(playerId);
75
+ playerController.destroy?.();
70
76
  };
71
77
  }, [playerId, playerController]);
72
78
 
@@ -9,6 +9,7 @@ type PlayerState = {
9
9
  isLive: boolean;
10
10
  isPaused: boolean;
11
11
  isBuffering: boolean;
12
+ isReadyToPlay: boolean;
12
13
  };
13
14
 
14
15
  export const usePlayerState = (
@@ -20,12 +21,14 @@ export const usePlayerState = (
20
21
  const [seekableDuration, setSeekableDuration] = React.useState<number>(null);
21
22
  const [isLive, setIsLive] = React.useState<boolean>(null);
22
23
  const [isBuffering, setIsBuffering] = React.useState<boolean>(false);
24
+ const [isReadyToPlay, setIsReadyToPlay] = React.useState<boolean>(false);
23
25
  const [isPaused, setIsPaused] = React.useState<boolean>(false);
24
26
 
25
27
  const player: Player = usePlayer(playerId);
26
28
 
27
29
  const onPlayerChangeState = React.useCallback(() => {
28
30
  setIsBuffering(player.isBuffering());
31
+ setIsReadyToPlay(player.isReadyToPlay);
29
32
  setCurrentTime(player.getContentPosition());
30
33
  setDuration(player.getDuration());
31
34
  setSeekableDuration(player.getSeekableDuration());
@@ -60,7 +63,16 @@ export const usePlayerState = (
60
63
  isLive,
61
64
  isPaused,
62
65
  isBuffering,
66
+ isReadyToPlay,
63
67
  }),
64
- [currentTime, duration, seekableDuration, isLive, isPaused, isBuffering]
68
+ [
69
+ currentTime,
70
+ duration,
71
+ seekableDuration,
72
+ isLive,
73
+ isPaused,
74
+ isBuffering,
75
+ isReadyToPlay,
76
+ ]
65
77
  );
66
78
  };
@@ -0,0 +1,50 @@
1
+ import { useEffect, useState } from "react";
2
+ import { OverlaysObserver } from "./OverlayObserver/OverlaysObserver";
3
+ import { useFeedLoader } from "../../reactHooks";
4
+ import { usePlayer } from "./usePlayer";
5
+
6
+ export const useTitleSummaryOverlay = (
7
+ feedUrl,
8
+ entry,
9
+ isLive = false,
10
+ interval: number
11
+ ) => {
12
+ const player = usePlayer();
13
+ const [title, setTitle] = useState<string | number>(entry?.title || "");
14
+ const [summary, setSummary] = useState<string | number>(entry?.summary || "");
15
+
16
+ // Hook to load feed data
17
+ const { data: feedData, reloadData } = useFeedLoader({ feedUrl });
18
+
19
+ useEffect(() => {
20
+ if (player && feedUrl && isLive) {
21
+ const observer = new OverlaysObserver({ player });
22
+
23
+ observer.setFeedDataHandlers(feedUrl, reloadData, interval, (entry) => {
24
+ setTitle(entry?.title || "");
25
+ setSummary(entry?.summary || "");
26
+ });
27
+
28
+ const subscription = observer
29
+ .getTitleSummaryObservable()
30
+ .subscribe(({ title, summary }) => {
31
+ setTitle(title);
32
+ setSummary(summary);
33
+ });
34
+
35
+ return () => {
36
+ subscription.unsubscribe();
37
+ observer.clearFeedDataInterval();
38
+ };
39
+ }
40
+ }, [player, feedUrl, reloadData, isLive]);
41
+
42
+ useEffect(() => {
43
+ if (isLive && feedData?.entry?.[0]) {
44
+ setTitle(feedData.entry[0].title || "");
45
+ setSummary(feedData.entry[0].summary || "");
46
+ }
47
+ }, [feedData]);
48
+
49
+ return { title, summary };
50
+ };