@applicaster/quick-brick-player 12.0.0-alpha.5040434238 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/quick-brick-player",
3
- "version": "12.0.0-alpha.5040434238",
3
+ "version": "12.0.0-alpha.5050333009",
4
4
  "description": "Quick Brick Player",
5
5
  "main": "./src/index.ts",
6
6
  "types": "index.d.ts",
@@ -36,11 +36,11 @@
36
36
  },
37
37
  "homepage": "https://github.com/applicaster/quickbrick#readme",
38
38
  "dependencies": {
39
- "@applicaster/quick-brick-mobile-transport-controls": "12.0.0-alpha.5040434238",
40
- "@applicaster/quick-brick-tv-transport-controls": "11.0.0-rc.133",
41
- "@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.5040434238",
42
- "@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.5040434238",
43
- "@applicaster/zapp-react-native-utils": "12.0.0-alpha.5040434238",
39
+ "@applicaster/quick-brick-mobile-transport-controls": "12.0.0-rc.96",
40
+ "@applicaster/quick-brick-tv-transport-controls": "12.0.0-alpha.5050333009",
41
+ "@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.5050333009",
42
+ "@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.5050333009",
43
+ "@applicaster/zapp-react-native-utils": "12.0.0-alpha.5050333009",
44
44
  "keymirror": "^0.1.1",
45
45
  "query-string": "7.1.3",
46
46
  "react-native-web-linear-gradient": "1.1.2",
@@ -32,6 +32,7 @@ import {
32
32
  AnimatedScrollModal,
33
33
  } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
34
34
  import { PADDING_HORIZONTAL } from "./Layout/defaultLayoutsConts";
35
+ import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
35
36
 
36
37
  const { log_debug } = createLogger({
37
38
  subsystem: "UIComponent",
@@ -294,7 +295,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
294
295
  playerState.isPaused ? player?.play() : player.pause();
295
296
  }, [playerState, player]);
296
297
 
297
- const seekDuration = Number(value("seek_duration"));
298
+ const seekDuration = toNumberWithDefault(10, value("seek_duration"));
298
299
 
299
300
  const onRewindButtonPress = React.useCallback(() => {
300
301
  player?.rewind(seekDuration);
@@ -377,10 +378,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
377
378
  player?.seekTo(0);
378
379
  }, [player]);
379
380
 
380
- const edges = React.useMemo<Edge[]>(
381
- () => (["right", "left", "top"]),
382
- [docked]
383
- );
381
+ const edges = React.useMemo<Edge[]>(() => ["right", "left", "top"], [docked]);
384
382
 
385
383
  const ContainerView = SafeAreaView;
386
384
 
@@ -390,12 +388,10 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
390
388
  <AnimationComponent
391
389
  animationType={ComponentAnimationType.moveUpComponent}
392
390
  additionalData={{ saveArea: true }}
391
+ style={Styles.playerContainer}
393
392
  >
394
393
  <AnimatedScrollModal>
395
- <View
396
- style={Styles.playerContainer}
397
- onLayout={onLayout}
398
- >
394
+ <View style={Styles.playerContainer} onLayout={onLayout}>
399
395
  <Layout
400
396
  entry={entry}
401
397
  playerState={playerState}
@@ -426,7 +422,9 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
426
422
  />
427
423
  <View style={Styles.paddingHorizontal}>
428
424
  {children}
429
- <AnimationComponent animationType={ComponentAnimationType.componentFade}>
425
+ <AnimationComponent
426
+ animationType={ComponentAnimationType.componentFade}
427
+ >
430
428
  <PlayerDetails
431
429
  configuration={configuration}
432
430
  style={Styles.playerDetails}
@@ -1,6 +1,7 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
3
  import { View } from "react-native";
4
+ import { dayjs } from "@applicaster/zapp-react-native-utils/dateUtils";
4
5
 
5
6
  import {
6
7
  ProgressBar,
@@ -27,8 +28,13 @@ import { PlayerImageBackground } from "@applicaster/zapp-react-native-ui-compone
27
28
  import { useLanguageData } from "@applicaster/zapp-react-native-utils/reactHooks/player";
28
29
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
29
30
  import { useEffect, useMemo, useState } from "react";
30
- import { useModalAnimationContext, AnimationComponent, ComponentAnimationType } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
31
+ import {
32
+ useModalAnimationContext,
33
+ AnimationComponent,
34
+ ComponentAnimationType,
35
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
31
36
  import DockedLayout from "./DockedLayout";
37
+ import { isValidDate } from "../../Utils";
32
38
 
33
39
  type Dimensions = {
34
40
  width: number;
@@ -75,7 +81,7 @@ type LayoutProps = {
75
81
 
76
82
  const Styles = {
77
83
  container: { flex: 1 },
78
- tabletContainer: (isPortraitMode) => ({
84
+ tabletContainer: (isPortraitMode: boolean) => ({
79
85
  flex: 1,
80
86
  alignItems: "center",
81
87
  justifyContent: isPortraitMode ? "center" : "flex-start",
@@ -95,19 +101,31 @@ const Styles = {
95
101
  justifyContent: isLiveShowFullControls ? "space-between" : "space-evenly",
96
102
  }),
97
103
  backgroundImageContainer: (dimensions: Dimensions, isTablet: boolean) => {
98
- const mobileValue = dimensions.width ? dimensions.width - (PADDING_HORIZONTAL * 2) : 330;
104
+ const mobileValue = dimensions.width
105
+ ? dimensions.width - PADDING_HORIZONTAL * 2
106
+ : 330;
99
107
 
100
108
  return {
101
109
  width: isTablet ? 463 : mobileValue,
102
110
  height: isTablet ? 463 : mobileValue,
103
111
  };
104
112
  },
105
- backgroundImageContainerImageStyle: (isTablet, docked, startComponentsAnimation, isActiveGesture) => ({
106
- borderRadius: (startComponentsAnimation && isActiveGesture) || docked ? 0 : (isTablet ? 24 : 10),
113
+ backgroundImageContainerImageStyle: (
114
+ isTablet: boolean,
115
+ docked: boolean,
116
+ startComponentsAnimation: boolean,
117
+ isActiveGesture: boolean
118
+ ) => ({
119
+ borderRadius:
120
+ (startComponentsAnimation && isActiveGesture) || docked
121
+ ? 0
122
+ : isTablet
123
+ ? 24
124
+ : 10,
107
125
  }),
108
126
 
109
127
  paddingHorizontal: {
110
- paddingHorizontal: PADDING_HORIZONTAL
128
+ paddingHorizontal: PADDING_HORIZONTAL,
111
129
  },
112
130
 
113
131
  controlsMobile: {
@@ -174,8 +192,31 @@ export default function DefaultLayout(props: LayoutProps) {
174
192
 
175
193
  let showFullControls = !controller?.isAd();
176
194
 
195
+ const startTime = entry.extensions?.live?.start_time;
196
+ const endTime = entry.extensions?.live?.end_time;
197
+
198
+ const hasValidStartTime = isValidDate(startTime);
199
+ const hasValidEndTime = isValidDate(endTime);
200
+
201
+ const isDateBasedLiveContent =
202
+ isLiveContent && hasValidStartTime && hasValidEndTime;
203
+
204
+ const needsToShowProgressBar =
205
+ (isLiveContent &&
206
+ (Boolean(value("liveSeekingEnabled")) || isDateBasedLiveContent)) ||
207
+ !isLiveContent;
208
+
209
+ const startTimestamp = hasValidStartTime
210
+ ? dayjs.utc(startTime!).valueOf()
211
+ : undefined;
212
+
213
+ const endTimestamp = hasValidEndTime
214
+ ? dayjs.utc(endTime!).valueOf()
215
+ : undefined;
216
+
177
217
  if (isLiveContent) {
178
218
  const seekEnabled = Boolean(value("liveSeekingEnabled"));
219
+
179
220
  showFullControls = !!(controller?.hasSeekableDuration?.() && seekEnabled);
180
221
  }
181
222
 
@@ -205,12 +246,14 @@ export default function DefaultLayout(props: LayoutProps) {
205
246
 
206
247
  const rewind = rewindButton({
207
248
  isTablet,
208
- onPress: controller?.hasContentDuration?.() ? onRewindButtonPress : null,
249
+ onPress: controller?.hasSeekableDuration?.() ? onRewindButtonPress : null,
209
250
  });
210
251
 
211
252
  const forward = forwardButton({
212
253
  isTablet,
213
- onPress: controller?.hasContentDuration?.() ? onForwardButtonPress : null,
254
+ onPress: controller?.hasSeekableDuration?.()
255
+ ? onForwardButtonPress
256
+ : null,
214
257
  });
215
258
 
216
259
  const speed = speedButton({
@@ -239,7 +282,6 @@ export default function DefaultLayout(props: LayoutProps) {
239
282
  return [skipToStart, rewind, play, forward, skipToLive];
240
283
  }
241
284
 
242
- // Temporary empty button until we add sleep button. It will ensure the buttons will not break in their position
243
285
  return [speed, rewind, play, forward, sleep];
244
286
  }, [
245
287
  playerState,
@@ -260,15 +302,12 @@ export default function DefaultLayout(props: LayoutProps) {
260
302
 
261
303
  useEffect(() => {
262
304
  if (showCounter && sleepTimerDate !== 0) {
263
- // Setting the initial timeLeft based on sleepTimerDate
264
305
  setTimeLeft(sleepTimerDate - Date.now());
265
306
 
266
- // Setting up an interval to update the remaining time every second
267
307
  const intervalId = setInterval(() => {
268
308
  setTimeLeft(() => {
269
309
  const newTimeLeft = sleepTimerDate - Date.now();
270
310
 
271
- // If the new remaining time is less than or equal to 0, clear the interval and trigger the cancelSleepTimer function
272
311
  if (newTimeLeft <= 0) {
273
312
  clearInterval(intervalId);
274
313
 
@@ -276,31 +315,28 @@ export default function DefaultLayout(props: LayoutProps) {
276
315
 
277
316
  return 0;
278
317
  } else {
279
- // Otherwise, update the state with the new remaining time
280
318
  return newTimeLeft;
281
319
  }
282
320
  });
283
321
  }, 1000);
284
322
 
285
- // Clearing the interval when the component unmounts or dependencies change
286
323
  return () => clearInterval(intervalId);
287
324
  } else {
288
- // Ensure the timeLeft state is set correctly if showCounter is false
289
325
  setTimeLeft(0);
290
326
  }
291
327
  }, [sleepTimerDate, showCounter, cancelSleepTimer]);
292
328
 
293
329
  const Separator = () => <View style={Styles.separator} />;
294
330
 
295
- const liveSeekingEnabled = value("liveSeekingEnabled");
296
-
297
- const needsToShowProgressBar =
298
- (isLiveContent && liveSeekingEnabled) || !isLiveContent;
331
+ const defaultImageDimensions = React.useMemo(
332
+ () =>
333
+ defaultDimensions &&
334
+ Styles.backgroundImageContainer(defaultDimensions, isTablet),
335
+ [defaultDimensions, isTablet]
336
+ );
299
337
 
300
- const defaultImageDimensions = React.useMemo(() =>
301
- defaultDimensions && Styles.backgroundImageContainer(defaultDimensions, isTablet), [defaultDimensions]);
302
-
303
- const { isActiveGesture, startComponentsAnimation } = useModalAnimationContext();
338
+ const { isActiveGesture, startComponentsAnimation } =
339
+ useModalAnimationContext();
304
340
 
305
341
  return (
306
342
  <View
@@ -318,34 +354,50 @@ export default function DefaultLayout(props: LayoutProps) {
318
354
  actionButtons={actionButtons}
319
355
  />
320
356
  </AnimationComponent>
321
- <AnimationComponent animationType={ComponentAnimationType.moveUpComponent}
322
- style={isTablet ? Styles.playerImageWrapper : null}>
323
- <AnimationComponent animationType={ComponentAnimationType.moveComponentHorizontal}
324
- additionalData={
325
- {
326
- disableAnimatedComponent: !isTablet,
327
- moveValue: (dimensions.width - 463) / 2,
328
- extraAnimation: !isTablet ? {
329
- [ComponentAnimationType.audioPlayerPaddingHorizontal]: {
330
- style: Styles.paddingHorizontal,
357
+ <AnimationComponent
358
+ animationType={ComponentAnimationType.moveUpComponent}
359
+ style={isTablet ? Styles.playerImageWrapper : undefined}
360
+ >
361
+ <AnimationComponent
362
+ animationType={ComponentAnimationType.moveComponentHorizontal}
363
+ additionalData={{
364
+ disableAnimatedComponent: !isTablet,
365
+ moveValue: (dimensions.width - 463) / 2,
366
+ extraAnimation: !isTablet
367
+ ? {
368
+ [ComponentAnimationType.audioPlayerPaddingHorizontal]: {
369
+ style: Styles.paddingHorizontal,
370
+ },
331
371
  }
332
- } : null
333
- }
334
- }
335
- style={isTablet ? Styles.alignItemsCenter : null}>
372
+ : null,
373
+ }}
374
+ style={isTablet ? Styles.alignItemsCenter : undefined}
375
+ >
336
376
  <PlayerImageBackground
337
377
  entry={entry}
338
378
  style={Styles.backgroundImageContainer(dimensions, isTablet)}
339
- imageStyle={Styles.backgroundImageContainerImageStyle(isTablet, docked, startComponentsAnimation, isActiveGesture)}
379
+ imageStyle={Styles.backgroundImageContainerImageStyle(
380
+ isTablet,
381
+ docked,
382
+ startComponentsAnimation,
383
+ isActiveGesture
384
+ )}
340
385
  defaultImageDimensions={defaultImageDimensions}
341
386
  />
342
387
  </AnimationComponent>
343
388
 
344
389
  <DockedLayout {...props} />
345
390
  </AnimationComponent>
346
- <View style={[Styles.paddingHorizontal, isTablet ? Styles.tabletLayout : null]}>
347
- {!docked &&
348
- <AnimationComponent animationType={ComponentAnimationType.componentFade}>
391
+ <View
392
+ style={[
393
+ Styles.paddingHorizontal,
394
+ isTablet ? Styles.tabletLayout : undefined,
395
+ ]}
396
+ >
397
+ {!docked && (
398
+ <AnimationComponent
399
+ animationType={ComponentAnimationType.componentFade}
400
+ >
349
401
  <AudioDescription
350
402
  title={title}
351
403
  summary={summary}
@@ -364,6 +416,10 @@ export default function DefaultLayout(props: LayoutProps) {
364
416
  playerState={playerState}
365
417
  disableFullscreen={true}
366
418
  sliderStyle={2}
419
+ startTimestamp={startTimestamp}
420
+ endTimestamp={endTimestamp}
421
+ isDateBasedLiveContent={isDateBasedLiveContent}
422
+ onSkipLive={onSkipLive}
367
423
  />
368
424
 
369
425
  {showCounter && timeLeft >= 0 && (
@@ -382,11 +438,14 @@ export default function DefaultLayout(props: LayoutProps) {
382
438
  visible={true}
383
439
  keepAlive={null}
384
440
  buttons={controlButtons}
385
- style={Styles.controls(isTablet, isLiveContent && showFullControls)}
441
+ style={Styles.controls(
442
+ isTablet,
443
+ isLiveContent && showFullControls
444
+ )}
386
445
  playerState={playerState}
387
446
  />
388
447
  </AnimationComponent>
389
- }
448
+ )}
390
449
  </View>
391
450
  </View>
392
451
  );
@@ -109,18 +109,24 @@ const backgroundColorStyle = (value: GetValue): ViewStyle => ({
109
109
  backgroundColor: value("docked_player_background_color"),
110
110
  });
111
111
 
112
- const playerDimensionsStyle = (minimisedHeight: number, addPadding: boolean): ViewStyle => ({
113
- paddingLeft: addPadding ? (minimisedHeight || 100) : 0,
112
+ const playerDimensionsStyle = (
113
+ minimisedHeight: number,
114
+ addPadding: boolean
115
+ ): ViewStyle => ({
116
+ paddingLeft: addPadding ? minimisedHeight || 100 : 0,
114
117
  });
115
118
 
116
119
  const animatedContainerStyle = (docked: boolean): ViewStyle => ({
117
- top: docked ? 2 : null,
118
120
  position: "absolute",
119
121
  width: "100%",
120
122
  zIndex: docked ? 1 : -1,
121
123
  });
122
124
 
123
- const backgroundImageStyle = (dimensions: Dimensions, minimisedHeight: number, isActiveGesture: boolean): ViewStyle => ({
125
+ const backgroundImageStyle = (
126
+ dimensions: Dimensions,
127
+ minimisedHeight: number,
128
+ isActiveGesture: boolean
129
+ ): ViewStyle => ({
124
130
  height: isActiveGesture ? minimisedHeight : dimensions.height,
125
131
  aspectRatio: 1,
126
132
  position: "absolute",
@@ -215,32 +221,60 @@ export default function DockedLayout(props: LayoutProps) {
215
221
 
216
222
  const visible = true;
217
223
  const liveButton = Boolean(value("live_badge_enabled"));
218
- const { isActiveGesture, setPlayerAnimationState, startComponentsAnimation, minimisedHeight } = useModalAnimationContext();
219
- const maximazeVideoModal = React.useCallback(() => setPlayerAnimationState(PlayerAnimationStateEnum.maximaze), []);
224
+
225
+ const {
226
+ isActiveGesture,
227
+ setPlayerAnimationState,
228
+ startComponentsAnimation,
229
+ minimisedHeight,
230
+ } = useModalAnimationContext();
231
+
232
+ const maximazeVideoModal = React.useCallback(
233
+ () => setPlayerAnimationState(PlayerAnimationStateEnum.maximaze),
234
+ []
235
+ );
220
236
 
221
237
  if (!layoutState.docked && !startComponentsAnimation) return null;
222
-
238
+
223
239
  return (
224
- <AnimationComponent animationType={ComponentAnimationType.componentAppears}
225
- style={[styles.container, animatedContainerStyle(isActiveGesture || layoutState.docked)]}
240
+ <AnimationComponent
241
+ animationType={ComponentAnimationType.componentAppears}
242
+ style={[
243
+ styles.container,
244
+ animatedContainerStyle(isActiveGesture || layoutState.docked),
245
+ ]}
226
246
  >
227
- {!playerState.isLive && layoutState.docked && (
228
- <ProgressBar
229
- content={entry}
230
- value={value}
231
- visible={visible}
232
- layoutState={layoutState}
233
- onPlayerSeek={onPlayerSeek}
234
- playerState={playerState}
235
- docked
236
- />
237
- )}
247
+ {!playerState.isLive &&
248
+ (layoutState.docked || startComponentsAnimation) && (
249
+ <ProgressBar
250
+ content={entry}
251
+ value={value}
252
+ visible={visible}
253
+ layoutState={layoutState}
254
+ onPlayerSeek={onPlayerSeek}
255
+ playerState={playerState}
256
+ startComponentsAnimation={startComponentsAnimation}
257
+ docked
258
+ />
259
+ )}
238
260
 
239
261
  <View style={styles.content}>
240
- <View style={backgroundImageStyle(dimensions, minimisedHeight, isActiveGesture)}/>
262
+ <View
263
+ style={backgroundImageStyle(
264
+ dimensions,
265
+ minimisedHeight,
266
+ isActiveGesture
267
+ )}
268
+ />
241
269
  <TouchableWithoutFeedback onPress={maximazeVideoModal}>
242
270
  <View
243
- style={[styles.touchableArea, playerDimensionsStyle(minimisedHeight, isActiveGesture || layoutState.docked)]}
271
+ style={[
272
+ styles.touchableArea,
273
+ playerDimensionsStyle(
274
+ minimisedHeight,
275
+ isActiveGesture || layoutState.docked
276
+ ),
277
+ ]}
244
278
  >
245
279
  <View
246
280
  style={[
@@ -1,7 +1,6 @@
1
- // Disabling for now as lint can't recognise typescript definition here
2
1
  /* eslint-disable react/prop-types */
3
2
  import * as React from "react";
4
- import { StyleSheet, View } from "react-native";
3
+ import { Animated, StyleSheet, View } from "react-native";
5
4
  import LinearGradient from "../LinearGradient";
6
5
  import { getDominantColor, getPalettes } from "../../../utils/utils";
7
6
  import { useEffect } from "react";
@@ -11,7 +10,7 @@ type MagicBGProps = {
11
10
  image: string;
12
11
  fallbackUrl?: string;
13
12
  defaultColor: string;
14
- colors: string[]; // Updated to type string[] as it holds hex values
13
+ colors: string[];
15
14
  };
16
15
 
17
16
  const defaultColors = {
@@ -66,31 +65,46 @@ export const MagicBG: React.FC<MagicBGProps> = ({
66
65
  defaultColors.defaultGradientEnd,
67
66
  ]);
68
67
 
68
+ const [backgroundColorAnim] = React.useState(new Animated.Value(0)); // from 0 to 1
69
+ const [gradientOpacityAnim] = React.useState(new Animated.Value(0)); // from 0 to 1
70
+
69
71
  useEffect(() => {
72
+ const animateColors = () => {
73
+ Animated.timing(backgroundColorAnim, {
74
+ toValue: 1,
75
+ duration: 500,
76
+ useNativeDriver: false,
77
+ }).start();
78
+
79
+ Animated.timing(gradientOpacityAnim, {
80
+ toValue: 1,
81
+ duration: 500,
82
+ useNativeDriver: false,
83
+ }).start();
84
+ };
85
+
70
86
  const fetchColors = async () => {
71
- // Check if colors are provided and not empty
72
87
  if (colors && colors.length > 0) {
73
- // Use provided colors for gradient and dominant color
74
- setGradientColors(colors.slice(0, 2)); // First 2 colors for gradient
75
- setDominantColor(colors[0]); // First color as dominant
88
+ setGradientColors(colors.slice(0, 2));
89
+ setDominantColor(colors[0]);
90
+ animateColors();
76
91
  } else if (image) {
77
- // If no colors, try fetching from image
78
92
  try {
79
93
  const { gradientColors, dominantColor } =
80
94
  await fetchPaletteAndDominantColor(image);
81
95
 
82
96
  setGradientColors(gradientColors);
83
97
  setDominantColor(dominantColor);
98
+ animateColors();
84
99
  } catch (error) {
85
- // If image fetch fails, try fallback URL
86
100
  try {
87
101
  const { gradientColors, dominantColor } =
88
102
  await fetchPaletteAndDominantColor(fallbackUrl);
89
103
 
90
104
  setGradientColors(gradientColors);
91
105
  setDominantColor(dominantColor);
106
+ animateColors();
92
107
  } catch {
93
- // If everything fails, use default color
94
108
  setGradientColors([
95
109
  defaultColors.defaultGradientStart,
96
110
  defaultColors.defaultGradientEnd,
@@ -100,7 +114,6 @@ export const MagicBG: React.FC<MagicBGProps> = ({
100
114
  }
101
115
  }
102
116
  } else {
103
- // If no image or colors, use default color
104
117
  setGradientColors([
105
118
  defaultColors.defaultGradientStart,
106
119
  defaultColors.defaultGradientEnd,
@@ -111,13 +124,32 @@ export const MagicBG: React.FC<MagicBGProps> = ({
111
124
  };
112
125
 
113
126
  fetchColors();
114
- }, [image, fallbackUrl, colors, defaultColor]);
127
+ }, []);
128
+
129
+ const interpolatedBackgroundColor = backgroundColorAnim.interpolate({
130
+ inputRange: [0, 1],
131
+ outputRange: ["black", dominantColor],
132
+ });
115
133
 
116
134
  return (
117
- <View style={[styles.container, { backgroundColor: dominantColor }]}>
135
+ <Animated.View
136
+ style={[
137
+ styles.container,
138
+ { backgroundColor: interpolatedBackgroundColor },
139
+ ]}
140
+ >
118
141
  <View style={styles.fullScreenOverlay} />
119
- <LinearGradient colors={gradientColors} style={styles.gradientOverlay} />
142
+
143
+ <Animated.View
144
+ style={[styles.gradientOverlay, { opacity: gradientOpacityAnim }]}
145
+ >
146
+ <LinearGradient
147
+ colors={gradientColors}
148
+ style={StyleSheet.absoluteFill}
149
+ />
150
+ </Animated.View>
151
+
120
152
  {children}
121
- </View>
153
+ </Animated.View>
122
154
  );
123
155
  };
@@ -1,26 +1,5 @@
1
1
  import queryString from "query-string";
2
-
3
- export const toTypeString = (obj) => {
4
- switch (typeof obj) {
5
- case "object":
6
- return obj instanceof Date ? obj.toISOString() : JSON.stringify(obj); // object, null
7
- case "undefined":
8
- return "";
9
- default:
10
- // boolean, number, string
11
- return obj.toString();
12
- }
13
- };
14
-
15
- export const stringsOnlyObject = (obj) => {
16
- const strObj = {};
17
-
18
- Object.keys(obj).forEach((x) => {
19
- strObj[x] = toTypeString(obj[x]);
20
- });
21
-
22
- return strObj;
23
- };
2
+ import { dayjs } from "@applicaster/zapp-react-native-utils/dateUtils";
24
3
 
25
4
  export const getLiveUrl = (url: string) => {
26
5
  // Use the replace method with a regular expression to replace all instances of subClass with livePage
@@ -42,3 +21,11 @@ export const addQueryParam = (
42
21
 
43
22
  return `${urlWithoutQuery}?${updatedQueryString}`;
44
23
  };
24
+
25
+ export const isValidDate = (dateString: string | undefined): boolean => {
26
+ return (
27
+ typeof dateString === "string" &&
28
+ dateString.trim() !== "" &&
29
+ dayjs.utc(dateString).isValid()
30
+ );
31
+ };
@@ -4,7 +4,7 @@
4
4
  import React from "react";
5
5
  import * as R from "ramda";
6
6
 
7
- import { findNodeHandle, NativeModules, StyleSheet, View } from "react-native";
7
+ import { findNodeHandle, StyleSheet, View } from "react-native";
8
8
  import TransportControlsMobile from "@applicaster/quick-brick-mobile-transport-controls";
9
9
  import TransportControlsTV from "@applicaster/quick-brick-tv-transport-controls";
10
10
 
@@ -38,16 +38,11 @@ import { getAllAccessibilityProps } from "@applicaster/zapp-react-native-utils/c
38
38
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
39
39
  import { getTargetScreenDataFromEntry } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useTargetScreenData";
40
40
  import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
41
- import { AnimationComponent, ComponentAnimationType } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
42
-
43
- const RNPlayerManager = {
44
- QuickBrickPlayerPlugin: isApplePlatform()
45
- ? NativeModules.PlayerModule
46
- : NativeModules.QuickBrickDefaultPlayerModule,
47
- KalturaPlayerPlugin: isApplePlatform()
48
- ? NativeModules.KalturaPlayerModule
49
- : NativeModules.QBKalturaPlayerModule,
50
- };
41
+ import {
42
+ AnimationComponent,
43
+ ComponentAnimationType,
44
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
45
+ import { RNPlayerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/nativePlayerManager";
51
46
 
52
47
  const styles = StyleSheet.create({
53
48
  container: { flex: 1 },
@@ -117,8 +112,6 @@ export default class VideoPlayer extends React.Component<
117
112
  }
118
113
 
119
114
  componentWillUnmount() {
120
- this.stopBackgroundPlayback();
121
-
122
115
  this.playbackStarted = false;
123
116
  this.handleAnalyticEvent(GENERAL_EVENT.session_end);
124
117
  this.onPlayerClosed();
@@ -163,6 +156,11 @@ export default class VideoPlayer extends React.Component<
163
156
  }
164
157
  }
165
158
 
159
+ // For close button
160
+ public closeNativePlayer = () => {
161
+ this.stopBackgroundPlayback();
162
+ };
163
+
166
164
  getPlayNextData = () => this.props.playNextData as PlayNextData;
167
165
 
168
166
  /**
@@ -337,11 +335,7 @@ export default class VideoPlayer extends React.Component<
337
335
  }
338
336
  };
339
337
 
340
- /**
341
- * Sets either subtitle language, or audio language if we have a selected a new locale.
342
- * If no locale is set it defaults to system language for audio, and leaves subtitles unaltered.
343
- */
344
- setLanguage = (selected = null) => {
338
+ selectTrack = (selected) => {
345
339
  const { trackType, id, language } = selected || {};
346
340
 
347
341
  const isOff = language === "Off";
@@ -782,7 +776,6 @@ export default class VideoPlayer extends React.Component<
782
776
 
783
777
  render() {
784
778
  const {
785
- castAction,
786
779
  docked,
787
780
  entry,
788
781
  fullscreen,
@@ -792,8 +785,13 @@ export default class VideoPlayer extends React.Component<
792
785
  isTabletPortrait,
793
786
  muted,
794
787
  style,
788
+ castAction,
789
+ controller,
790
+ startComponentsAnimation,
795
791
  } = this.props;
796
792
 
793
+ const supportNativeCast = controller.supportNativeCast();
794
+
797
795
  const { audioTrackId, keyEvent, paused, seek, textTrackId, rate } =
798
796
  this.state;
799
797
 
@@ -855,10 +853,14 @@ export default class VideoPlayer extends React.Component<
855
853
  !!this.props.controller?.supportsNativeControls?.();
856
854
 
857
855
  const controls = isAppleTV()
858
- ? supportsNativeControls && !isInlineTV && !this.getPlayNextData()
856
+ ? (supportsNativeControls && !isInlineTV) || !!this.getPlayNextData()
859
857
  : supportsNativeControls && !this.getPlayNextData();
860
858
 
861
- const shouldUseNativeControls = controls && !docked;
859
+ const shouldUseNativeControls =
860
+ controls &&
861
+ !startComponentsAnimation &&
862
+ !docked &&
863
+ !castAction?.state?.connected;
862
864
 
863
865
  platformSpecificProps = {
864
866
  ...platformSpecificProps,
@@ -987,7 +989,13 @@ export default class VideoPlayer extends React.Component<
987
989
  ...androidLifecyclePiPEvents,
988
990
  };
989
991
 
990
- const needToShowPlayNextOverlay = this.getPlayNextData() && !docked;
992
+ const needToShowPlayNextOverlay =
993
+ isTV() && this.getPlayNextData() && !docked;
994
+
995
+ const needToShowDefaultControls = !(
996
+ (controls && !castAction?.state?.connected) ||
997
+ this.getPlayNextData()
998
+ );
991
999
 
992
1000
  const playerContainerStyles = isApplePlatform()
993
1001
  ? styles.playerContainerBlack
@@ -1029,6 +1037,7 @@ export default class VideoPlayer extends React.Component<
1029
1037
  extraInfoText={castAction?.state?.friendlyStatus}
1030
1038
  controlsAlwaysVisible={castAction?.state?.connected}
1031
1039
  playNextData={this.getPlayNextData()}
1040
+ needToShowDefaultControls={needToShowDefaultControls}
1032
1041
  />
1033
1042
  ) : null;
1034
1043
  };
@@ -1063,29 +1072,14 @@ export default class VideoPlayer extends React.Component<
1063
1072
  const renderMobilePlayer = () => {
1064
1073
  return isFullScreenAudioPlayer && isAudioItem ? (
1065
1074
  <View style={containerStyle}>
1066
- {!castAction?.state?.connected && (
1075
+ {(!castAction?.state?.connected || supportNativeCast) && (
1067
1076
  <this.props.PlayerComponent
1068
1077
  ref={this._assignRoot}
1069
1078
  {...nativeEvents}
1070
1079
  {...nativeProps}
1071
1080
  />
1072
1081
  )}
1073
- <AudioPlayerWrapper {...sharedWrapperViewProps}>
1074
- <View>
1075
- {
1076
- <PlayNextOverlay
1077
- item={entry}
1078
- playerIsDocked={docked}
1079
- pluginConfiguration={this.getPluginConfiguration()}
1080
- playerId={this.props.playerId}
1081
- playNextData={this.getPlayNextData()}
1082
- setNextVideoPreloadThresholdPercentage={
1083
- this.props.setNextVideoPreloadThresholdPercentage
1084
- }
1085
- />
1086
- }
1087
- </View>
1088
- </AudioPlayerWrapper>
1082
+ <AudioPlayerWrapper {...sharedWrapperViewProps} />
1089
1083
  </View>
1090
1084
  ) : (
1091
1085
  <PlayerWrapper {...sharedWrapperViewProps}>
@@ -1100,11 +1094,24 @@ export default class VideoPlayer extends React.Component<
1100
1094
  <View style={styles.container}>
1101
1095
  <View style={[playerContainerStyles, playerDimensions]}>
1102
1096
  {isAudioItem && (
1103
- <AudioPlayer
1104
- style={playerDimensions}
1105
- audio_item={entry}
1106
- plugin_configuration={this.getPluginConfiguration()}
1107
- />
1097
+ <AnimationComponent
1098
+ animationType={ComponentAnimationType.player}
1099
+ additionalData={{
1100
+ ...playerDimensions,
1101
+ inlineAudioPlayer: true,
1102
+ }}
1103
+ style={getStyle(playerDimensions)}
1104
+ >
1105
+ <AudioPlayer
1106
+ style={
1107
+ isTV()
1108
+ ? getStyle(playerDimensions)
1109
+ : styles.playerSize
1110
+ }
1111
+ audio_item={entry}
1112
+ plugin_configuration={this.getPluginConfiguration()}
1113
+ />
1114
+ </AnimationComponent>
1108
1115
  )}
1109
1116
 
1110
1117
  {shouldRenderBackground && (
@@ -1116,7 +1123,7 @@ export default class VideoPlayer extends React.Component<
1116
1123
  />
1117
1124
  )}
1118
1125
 
1119
- {!castAction?.state?.connected && (
1126
+ {(!castAction?.state?.connected || supportNativeCast) && (
1120
1127
  <AnimationComponent
1121
1128
  animationType={ComponentAnimationType.player}
1122
1129
  additionalData={playerDimensions}
@@ -1127,7 +1134,11 @@ export default class VideoPlayer extends React.Component<
1127
1134
  {...nativeEvents}
1128
1135
  {...nativeProps}
1129
1136
  listener={this.props.listener}
1130
- style={isTV() ? getStyle(playerDimensions) : styles.playerSize}
1137
+ style={
1138
+ isTV()
1139
+ ? getStyle(playerDimensions)
1140
+ : styles.playerSize
1141
+ }
1131
1142
  pluginConfiguration={this.getPluginConfiguration()}
1132
1143
  controller={this.props.controller}
1133
1144
  >