@applicaster/quick-brick-player 12.0.0-alpha.9151207838 → 12.0.0-alpha.9233494544

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.9151207838",
3
+ "version": "12.0.0-alpha.9233494544",
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.9151207838",
40
- "@applicaster/quick-brick-tv-transport-controls": "11.0.0-rc.133",
41
- "@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.9151207838",
42
- "@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.9151207838",
43
- "@applicaster/zapp-react-native-utils": "12.0.0-alpha.9151207838",
39
+ "@applicaster/quick-brick-mobile-transport-controls": "12.0.0-rc.51",
40
+ "@applicaster/quick-brick-tv-transport-controls": "12.0.0-alpha.9233494544",
41
+ "@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.9233494544",
42
+ "@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.9233494544",
43
+ "@applicaster/zapp-react-native-utils": "12.0.0-alpha.9233494544",
44
44
  "keymirror": "^0.1.1",
45
45
  "query-string": "7.1.3",
46
46
  "react-native-web-linear-gradient": "1.1.2",
@@ -1,8 +1,9 @@
1
1
  import React, { useEffect, useState } from "react";
2
- import { Image, StyleSheet, View } from "react-native";
2
+ import { StyleSheet, View } from "react-native";
3
3
  import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
4
4
  import { MUTED_USAGE } from "../const";
5
5
  import { LiveImageManager } from "@applicaster/zapp-react-native-ui-components/Components/VideoLive/LiveImageManager";
6
+ import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
6
7
 
7
8
  type Asset = string | undefined;
8
9
 
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { ViewStyle, View, ScrollView } from "react-native";
2
+ import { ViewStyle, View } from "react-native";
3
3
 
4
4
  import { SafeAreaView, Edge } from "react-native-safe-area-context";
5
5
  import { usePlayerState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerState";
@@ -17,8 +17,6 @@ import { PlayerDetails } from "@applicaster/zapp-react-native-ui-components/Comp
17
17
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
18
18
  import { useEffect, useState } from "react";
19
19
  import { MagicBG } from "./MagicBG";
20
- import { addQueryParam, getLiveUrl } from "../Utils";
21
- import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
22
20
  import {
23
21
  formatSpeed,
24
22
  hasDatePassed,
@@ -26,6 +24,14 @@ import {
26
24
  NAMESPACE,
27
25
  } from "./utils";
28
26
  import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
27
+ import { addQueryParam, getLiveUrl } from "../Utils";
28
+ import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
29
+ import {
30
+ AnimationComponent,
31
+ ComponentAnimationType,
32
+ AnimatedScrollModal,
33
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
34
+ import { PADDING_HORIZONTAL } from "./Layout/defaultLayoutsConts";
29
35
 
30
36
  const { log_debug } = createLogger({
31
37
  subsystem: "UIComponent",
@@ -48,7 +54,7 @@ type Configuration = {
48
54
  };
49
55
 
50
56
  type Props = {
51
- children: React.ReactNode;
57
+ children?: React.ReactNode;
52
58
  inline?: boolean;
53
59
  docked?: boolean;
54
60
  configuration: Configuration;
@@ -68,29 +74,20 @@ export const defaultAspectRatioWidth = (height: number): number =>
68
74
  (height / 9) * 16;
69
75
 
70
76
  const Styles = {
71
- mainContainer: (docked: boolean, style: ViewStyle) => {
77
+ mainContainer: (style: ViewStyle) => {
72
78
  const combinedStyle: ViewStyle = {
73
79
  flex: 1,
74
80
  };
75
81
 
76
- if (!docked) {
77
- style.paddingTop = 0;
78
- style.paddingHorizontal = 15;
79
- }
80
-
81
- return { ...combinedStyle, ...style };
82
- },
83
- playerContainer: (docked: boolean, style: ViewStyle) => {
84
- const combinedStyle: ViewStyle = {
85
- flex: 1,
82
+ return {
83
+ ...combinedStyle,
84
+ ...style,
86
85
  };
87
-
88
- if (docked) {
89
- combinedStyle.height = style.height;
90
- }
91
-
92
- return combinedStyle;
93
86
  },
87
+ playerContainer: {
88
+ flex: 1,
89
+ },
90
+ paddingHorizontal: { paddingTop: 0, paddingHorizontal: PADDING_HORIZONTAL },
94
91
 
95
92
  controlsMobile: {
96
93
  height: 50,
@@ -145,6 +142,17 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
145
142
 
146
143
  const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
147
144
 
145
+ // TODO: Have generic place for live url from configuration. Currently its hardcoded for client.
146
+
147
+ const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
148
+
149
+ const { title, summary } = useTitleSummaryOverlay(
150
+ feedUrl,
151
+ entry,
152
+ playerState?.isLive,
153
+ configuration?.audio_live_player_update_interval
154
+ );
155
+
148
156
  const [playbackSpeed, setPlaybackSpeed] = useState({
149
157
  asset: configuration["speed_1"],
150
158
  value: 1.0,
@@ -157,14 +165,8 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
157
165
 
158
166
  const [sleepTimerDate, setSleepTimerDate] = useState(0);
159
167
 
160
- const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
161
-
162
- const { title, summary } = useTitleSummaryOverlay(
163
- feedUrl,
164
- entry,
165
- playerState?.isLive,
166
- configuration?.audio_live_player_update_interval || 60
167
- );
168
+ const [defaultDimensions, setDefaultDimensions] =
169
+ React.useState<Dimensions>(undefined);
168
170
 
169
171
  const [dimensions, setDimensions] = React.useState<Dimensions>({
170
172
  width: 0,
@@ -179,9 +181,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
179
181
  const { width, height } = event.nativeEvent.layout;
180
182
 
181
183
  setDimensions({
182
- width: docked ? defaultAspectRatioWidth(height) : width,
184
+ width,
183
185
  height,
184
186
  });
187
+
188
+ if (!defaultDimensions) setDefaultDimensions({ width, height });
185
189
  };
186
190
 
187
191
  const cancelSleepTimer = async () => {
@@ -193,7 +197,6 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
193
197
  });
194
198
 
195
199
  setSleepTimerDate(0);
196
- player?.pause();
197
200
 
198
201
  await localStorage.removeItem("sleep", NAMESPACE);
199
202
  };
@@ -374,58 +377,64 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
374
377
  player?.seekTo(0);
375
378
  }, [player]);
376
379
 
377
- const edges = React.useMemo<Edge[]>(
378
- () => (docked ? [] : ["right", "left", "top"]),
379
- [docked]
380
- );
380
+ const edges = React.useMemo<Edge[]>(() => ["right", "left", "top"], [docked]);
381
381
 
382
- const ContainerView = docked ? View : SafeAreaView;
382
+ const ContainerView = SafeAreaView;
383
383
 
384
384
  const MainContent = React.useMemo(() => {
385
385
  return (
386
- <ContainerView style={Styles.mainContainer(docked, style)} edges={edges}>
387
- <ScrollView
388
- style={Styles.playerContainer(docked, style)}
389
- onLayout={onLayout}
386
+ <ContainerView style={Styles.mainContainer(style)} edges={edges}>
387
+ <AnimationComponent
388
+ animationType={ComponentAnimationType.moveUpComponent}
389
+ additionalData={{ saveArea: true }}
390
390
  >
391
- <Layout
392
- entry={entry}
393
- playerState={playerState}
394
- layoutState={layoutState}
395
- value={value}
396
- onPlaybackButtonPress={onPlaybackButtonPress}
397
- onRewindButtonPress={onRewindButtonPress}
398
- onForwardButtonPress={onForwardButtonPress}
399
- onPlayerSeek={onPlayerSeek}
400
- onSkipStart={onSkipStart}
401
- onSpeedButtonPress={onSpeedButtonPress}
402
- onSleepButtonPress={onSleepButtonPress}
403
- playbackSpeedName={formatSpeed(playbackSpeed.value)}
404
- playbackSleepName={
405
- sleepTimer.value === 0 ? "sleep_timer" : "sleep_timer_active"
406
- }
407
- sleepTimerDate={sleepTimerDate}
408
- cancelSleepTimer={cancelSleepTimer}
409
- onSkipLive={onSkipLive}
410
- onCloseVideoModal={onCloseVideoModal}
411
- maximiseVideoModal={maximiseVideoModal}
412
- configuration={configuration}
413
- dimensions={dimensions}
414
- playNextData={playNextData}
415
- title={title}
416
- summary={summary}
417
- />
418
- {children}
419
- {!docked && (
420
- <PlayerDetails
421
- configuration={configuration}
422
- style={Styles.playerDetails}
423
- entry={entry}
424
- isTabletLandscape={isTabletLandscape}
425
- isAudioPlayer={true}
426
- />
427
- )}
428
- </ScrollView>
391
+ <AnimatedScrollModal>
392
+ <View style={Styles.playerContainer} onLayout={onLayout}>
393
+ <Layout
394
+ entry={entry}
395
+ playerState={playerState}
396
+ layoutState={layoutState}
397
+ value={value}
398
+ onPlaybackButtonPress={onPlaybackButtonPress}
399
+ onRewindButtonPress={onRewindButtonPress}
400
+ onForwardButtonPress={onForwardButtonPress}
401
+ onPlayerSeek={onPlayerSeek}
402
+ onSkipStart={onSkipStart}
403
+ onSpeedButtonPress={onSpeedButtonPress}
404
+ onSleepButtonPress={onSleepButtonPress}
405
+ playbackSpeedName={formatSpeed(playbackSpeed.value)}
406
+ playbackSleepName={
407
+ sleepTimer.value === 0 ? "sleep_timer" : "sleep_timer_active"
408
+ }
409
+ sleepTimerDate={sleepTimerDate}
410
+ cancelSleepTimer={cancelSleepTimer}
411
+ onSkipLive={onSkipLive}
412
+ onCloseVideoModal={onCloseVideoModal}
413
+ maximiseVideoModal={maximiseVideoModal}
414
+ configuration={configuration}
415
+ dimensions={dimensions}
416
+ playNextData={playNextData}
417
+ title={title}
418
+ summary={summary}
419
+ defaultDimensions={defaultDimensions}
420
+ />
421
+ <View style={Styles.paddingHorizontal}>
422
+ {children}
423
+ <AnimationComponent
424
+ animationType={ComponentAnimationType.componentFade}
425
+ >
426
+ <PlayerDetails
427
+ configuration={configuration}
428
+ style={Styles.playerDetails}
429
+ entry={entry}
430
+ isTabletLandscape={isTabletLandscape}
431
+ isAudioPlayer={true}
432
+ />
433
+ </AnimationComponent>
434
+ </View>
435
+ </View>
436
+ </AnimatedScrollModal>
437
+ </AnimationComponent>
429
438
  </ContainerView>
430
439
  );
431
440
  }, [
@@ -457,12 +466,17 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
457
466
  const queryParamName =
458
467
  configuration.audio_player_background_image_query || "width";
459
468
 
460
- const queryParamValue = 10;
469
+ const queryParamValue = 15;
461
470
 
462
471
  const url = addQueryParam(baseUrl, queryParamName, queryParamValue);
463
472
 
464
473
  return (
465
- <MagicBG image={url} fallbackUrl={baseUrl} defaultColor="#000000">
474
+ <MagicBG
475
+ image={url}
476
+ fallbackUrl={baseUrl}
477
+ colors={entry?.extensions?.backgroundColors}
478
+ defaultColor="#000000"
479
+ >
466
480
  {MainContent}
467
481
  </MagicBG>
468
482
  );
@@ -11,20 +11,28 @@ import {
11
11
 
12
12
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
13
13
 
14
- import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks/device/useIsTablet";
14
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
15
15
  import {
16
16
  forwardButton,
17
17
  playbackButton,
18
18
  rewindButton,
19
19
  skipToStartButton,
20
20
  skipToLiveButton,
21
+ PADDING_HORIZONTAL,
21
22
  speedButton,
22
23
  sleepButton,
23
24
  } from "./defaultLayoutsConts";
24
25
  import { AudioDescription } from "../AudioDescription";
25
26
  import { PlayerImageBackground } from "@applicaster/zapp-react-native-ui-components/Components/PlayerImageBackground";
26
27
  import { useLanguageData } from "@applicaster/zapp-react-native-utils/reactHooks/player";
28
+ import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
27
29
  import { useEffect, useMemo, useState } from "react";
30
+ import {
31
+ useModalAnimationContext,
32
+ AnimationComponent,
33
+ ComponentAnimationType,
34
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
35
+ import DockedLayout from "./DockedLayout";
28
36
 
29
37
  type Dimensions = {
30
38
  width: number;
@@ -60,9 +68,13 @@ type LayoutProps = {
60
68
  onPlayerSeek: (targetTime: number) => void;
61
69
  onSkipLive: () => void;
62
70
  onSkipStart: () => void;
71
+ onCloseVideoModal: () => void;
72
+ maximiseVideoModal: () => void;
63
73
  dimensions: Dimensions;
74
+ playNextData: PlayNextData;
64
75
  title: string | number;
65
76
  summary: string | number;
77
+ defaultDimensions: Dimensions;
66
78
  };
67
79
 
68
80
  const Styles = {
@@ -72,10 +84,13 @@ const Styles = {
72
84
  alignItems: "center",
73
85
  justifyContent: isPortraitMode ? "center" : "flex-start",
74
86
  }),
75
- tabletLayout: {
76
- width: 463,
87
+ playerImageWrapper: {
88
+ width: "100%",
77
89
  maxHeight: 750,
78
90
  marginTop: 80,
91
+ },
92
+ tabletLayout: {
93
+ width: 463,
79
94
  marginBottom: 80,
80
95
  },
81
96
  controls: (isTablet: boolean, isLiveShowFullControls: boolean) => ({
@@ -84,24 +99,31 @@ const Styles = {
84
99
  justifyContent: isLiveShowFullControls ? "space-between" : "space-evenly",
85
100
  }),
86
101
  backgroundImageContainer: (dimensions: Dimensions, isTablet: boolean) => {
102
+ const mobileValue = dimensions.width
103
+ ? dimensions.width - PADDING_HORIZONTAL * 2
104
+ : 330;
105
+
87
106
  return {
88
- width: isTablet ? 463 : dimensions.width || 330,
89
- height: isTablet ? 463 : dimensions.width || 330,
107
+ width: isTablet ? 463 : mobileValue,
108
+ height: isTablet ? 463 : mobileValue,
90
109
  };
91
110
  },
92
- backgroundImageContainerImageStyle: (isTablet) => ({
93
- borderRadius: isTablet ? 24 : 10,
111
+ backgroundImageContainerImageStyle: (
112
+ isTablet,
113
+ docked,
114
+ startComponentsAnimation,
115
+ isActiveGesture
116
+ ) => ({
117
+ borderRadius:
118
+ (startComponentsAnimation && isActiveGesture) || docked
119
+ ? 0
120
+ : isTablet
121
+ ? 24
122
+ : 10,
94
123
  }),
95
- playerContainer: (docked: boolean, style) => {
96
- const combinedStyle = {
97
- flex: 1,
98
- };
99
-
100
- if (docked) {
101
- combinedStyle["height"] = style.height;
102
- }
103
124
 
104
- return combinedStyle;
125
+ paddingHorizontal: {
126
+ paddingHorizontal: PADDING_HORIZONTAL,
105
127
  },
106
128
 
107
129
  controlsMobile: {
@@ -115,6 +137,12 @@ const Styles = {
115
137
  separator: {
116
138
  height: 20,
117
139
  },
140
+ flexDirectionRow: {
141
+ flexDirection: "row",
142
+ },
143
+ alignItemsCenter: {
144
+ alignItems: "center",
145
+ },
118
146
  };
119
147
 
120
148
  export default function DefaultLayout(props: LayoutProps) {
@@ -139,12 +167,14 @@ export default function DefaultLayout(props: LayoutProps) {
139
167
  sleepTimerDate,
140
168
  title,
141
169
  summary,
170
+ defaultDimensions,
142
171
  } = props;
143
172
 
144
173
  const isTablet = useIsTablet();
145
174
  const { width, height } = dimensions;
146
175
  const isPortraitMode = width < height;
147
176
  const isLiveContent = playerState.isLive;
177
+ const docked = layoutState.docked;
148
178
 
149
179
  const languageData = useLanguageData("audio-player");
150
180
 
@@ -251,7 +281,7 @@ export default function DefaultLayout(props: LayoutProps) {
251
281
 
252
282
  // Setting up an interval to update the remaining time every second
253
283
  const intervalId = setInterval(() => {
254
- setTimeLeft((prevTimeLeft) => {
284
+ setTimeLeft(() => {
255
285
  const newTimeLeft = sleepTimerDate - Date.now();
256
286
 
257
287
  // If the new remaining time is less than or equal to 0, clear the interval and trigger the cancelSleepTimer function
@@ -283,71 +313,120 @@ export default function DefaultLayout(props: LayoutProps) {
283
313
  const needsToShowProgressBar =
284
314
  (isLiveContent && liveSeekingEnabled) || !isLiveContent;
285
315
 
316
+ const defaultImageDimensions = React.useMemo(
317
+ () =>
318
+ defaultDimensions &&
319
+ Styles.backgroundImageContainer(defaultDimensions, isTablet),
320
+ [defaultDimensions]
321
+ );
322
+
323
+ const { isActiveGesture, startComponentsAnimation } =
324
+ useModalAnimationContext();
325
+
286
326
  return (
287
327
  <View
288
328
  style={
289
329
  isTablet ? Styles.tabletContainer(isPortraitMode) : Styles.container
290
330
  }
291
331
  >
292
- <TransportInfo
293
- content={entry}
294
- value={value}
295
- visible={true}
296
- languageData={languageData}
297
- layoutState={layoutState}
298
- actionButtons={actionButtons}
299
- />
300
- <View
301
- style={
302
- isTablet ? Styles.tabletContainer(isPortraitMode) : Styles.container
303
- }
332
+ <AnimationComponent animationType={ComponentAnimationType.componentFade}>
333
+ <TransportInfo
334
+ content={entry}
335
+ value={value}
336
+ visible={true}
337
+ languageData={languageData}
338
+ layoutState={layoutState}
339
+ actionButtons={actionButtons}
340
+ />
341
+ </AnimationComponent>
342
+ <AnimationComponent
343
+ animationType={ComponentAnimationType.moveUpComponent}
344
+ style={isTablet ? Styles.playerImageWrapper : null}
304
345
  >
305
- <View style={isTablet ? Styles.tabletLayout : null}>
346
+ <AnimationComponent
347
+ animationType={ComponentAnimationType.moveComponentHorizontal}
348
+ additionalData={{
349
+ disableAnimatedComponent: !isTablet,
350
+ moveValue: (dimensions.width - 463) / 2,
351
+ extraAnimation: !isTablet
352
+ ? {
353
+ [ComponentAnimationType.audioPlayerPaddingHorizontal]: {
354
+ style: Styles.paddingHorizontal,
355
+ },
356
+ }
357
+ : null,
358
+ }}
359
+ style={isTablet ? Styles.alignItemsCenter : null}
360
+ >
306
361
  <PlayerImageBackground
307
362
  entry={entry}
308
363
  style={Styles.backgroundImageContainer(dimensions, isTablet)}
309
- imageStyle={Styles.backgroundImageContainerImageStyle(isTablet)}
310
- />
311
- <AudioDescription
312
- title={title}
313
- summary={summary}
314
- value={value}
315
- layoutState={layoutState}
364
+ imageStyle={Styles.backgroundImageContainerImageStyle(
365
+ isTablet,
366
+ docked,
367
+ startComponentsAnimation,
368
+ isActiveGesture
369
+ )}
370
+ defaultImageDimensions={defaultImageDimensions}
316
371
  />
317
- <Separator />
318
- {needsToShowProgressBar && (
319
- <View>
320
- <ProgressBar
321
- content={entry}
322
- value={value}
323
- visible={true}
324
- layoutState={layoutState}
325
- onPlayerSeek={onPlayerSeek}
326
- playerState={playerState}
327
- disableFullscreen={true}
328
- sliderStyle={2}
329
- />
330
-
331
- {showCounter && timeLeft >= 0 && (
332
- <CountDown
333
- timeLeft={timeLeft}
372
+ </AnimationComponent>
373
+
374
+ <DockedLayout {...props} />
375
+ </AnimationComponent>
376
+ <View
377
+ style={[
378
+ Styles.paddingHorizontal,
379
+ isTablet ? Styles.tabletLayout : null,
380
+ ]}
381
+ >
382
+ {!docked && (
383
+ <AnimationComponent
384
+ animationType={ComponentAnimationType.componentFade}
385
+ >
386
+ <AudioDescription
387
+ title={title}
388
+ summary={summary}
389
+ value={value}
390
+ layoutState={layoutState}
391
+ />
392
+ <Separator />
393
+ {needsToShowProgressBar && (
394
+ <View>
395
+ <ProgressBar
396
+ content={entry}
334
397
  value={value}
335
- title={sleepTitle}
398
+ visible={true}
399
+ layoutState={layoutState}
400
+ onPlayerSeek={onPlayerSeek}
401
+ playerState={playerState}
402
+ disableFullscreen={true}
403
+ sliderStyle={2}
336
404
  />
337
- )}
338
405
 
339
- <Separator />
340
- </View>
341
- )}
342
- <Controls
343
- value={value}
344
- visible={true}
345
- keepAlive={null}
346
- buttons={controlButtons}
347
- style={Styles.controls(isTablet, isLiveContent && showFullControls)}
348
- playerState={playerState}
349
- />
350
- </View>
406
+ {showCounter && timeLeft >= 0 && (
407
+ <CountDown
408
+ timeLeft={timeLeft}
409
+ value={value}
410
+ title={sleepTitle}
411
+ />
412
+ )}
413
+
414
+ <Separator />
415
+ </View>
416
+ )}
417
+ <Controls
418
+ value={value}
419
+ visible={true}
420
+ keepAlive={null}
421
+ buttons={controlButtons}
422
+ style={Styles.controls(
423
+ isTablet,
424
+ isLiveContent && showFullControls
425
+ )}
426
+ playerState={playerState}
427
+ />
428
+ </AnimationComponent>
429
+ )}
351
430
  </View>
352
431
  </View>
353
432
  );
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import * as React from "react";
2
2
  import {
3
3
  StyleSheet,
4
4
  TouchableWithoutFeedback,
@@ -6,7 +6,7 @@ import {
6
6
  ViewStyle,
7
7
  } from "react-native";
8
8
  import * as R from "ramda";
9
- import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks";
9
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
10
10
 
11
11
  import {
12
12
  ContentInfo,
@@ -15,8 +15,13 @@ import {
15
15
  Button,
16
16
  } from "@applicaster/quick-brick-mobile-transport-controls";
17
17
 
18
- import { PlayerImageBackground } from "@applicaster/zapp-react-native-ui-components/Components/PlayerImageBackground";
19
18
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
19
+ import {
20
+ useModalAnimationContext,
21
+ AnimationComponent,
22
+ ComponentAnimationType,
23
+ PlayerAnimationStateEnum,
24
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
20
25
 
21
26
  type Dimensions = {
22
27
  width: number;
@@ -104,14 +109,26 @@ const backgroundColorStyle = (value: GetValue): ViewStyle => ({
104
109
  backgroundColor: value("docked_player_background_color"),
105
110
  });
106
111
 
107
- const playerDimensionsStyle = ({
108
- height,
109
- }: PlayerDimensionsProp): ViewStyle => ({
110
- paddingLeft: height || 100,
112
+ const playerDimensionsStyle = (
113
+ minimisedHeight: number,
114
+ addPadding: boolean
115
+ ): ViewStyle => ({
116
+ paddingLeft: addPadding ? minimisedHeight || 100 : 0,
117
+ });
118
+
119
+ const animatedContainerStyle = (docked: boolean): ViewStyle => ({
120
+ top: docked ? 2 : null,
121
+ position: "absolute",
122
+ width: "100%",
123
+ zIndex: docked ? 1 : -1,
111
124
  });
112
125
 
113
- const backgroundImageStyle = (dimensions: Dimensions): ViewStyle => ({
114
- height: dimensions.height,
126
+ const backgroundImageStyle = (
127
+ dimensions: Dimensions,
128
+ minimisedHeight: number,
129
+ isActiveGesture: boolean
130
+ ): ViewStyle => ({
131
+ height: isActiveGesture ? minimisedHeight : dimensions.height,
115
132
  aspectRatio: 1,
116
133
  position: "absolute",
117
134
  });
@@ -193,7 +210,6 @@ export default function DockedLayout(props: LayoutProps) {
193
210
  dimensions,
194
211
  entry,
195
212
  onCloseVideoModal,
196
- maximiseVideoModal,
197
213
  onPlayerSeek,
198
214
  playerState,
199
215
  configuration,
@@ -202,16 +218,34 @@ export default function DockedLayout(props: LayoutProps) {
202
218
  summary,
203
219
  } = props;
204
220
 
205
- const {
206
- deviceInfo: { isTablet },
207
- } = useDimensions("screen", { deviceInfo: true });
221
+ const isTablet = useIsTablet();
208
222
 
209
223
  const visible = true;
210
224
  const liveButton = Boolean(value("live_badge_enabled"));
211
225
 
226
+ const {
227
+ isActiveGesture,
228
+ setPlayerAnimationState,
229
+ startComponentsAnimation,
230
+ minimisedHeight,
231
+ } = useModalAnimationContext();
232
+
233
+ const maximazeVideoModal = React.useCallback(
234
+ () => setPlayerAnimationState(PlayerAnimationStateEnum.maximaze),
235
+ []
236
+ );
237
+
238
+ if (!layoutState.docked && !startComponentsAnimation) return null;
239
+
212
240
  return (
213
- <View style={styles.container}>
214
- {!playerState.isLive && (
241
+ <AnimationComponent
242
+ animationType={ComponentAnimationType.componentAppears}
243
+ style={[
244
+ styles.container,
245
+ animatedContainerStyle(isActiveGesture || layoutState.docked),
246
+ ]}
247
+ >
248
+ {!playerState.isLive && layoutState.docked && (
215
249
  <ProgressBar
216
250
  content={entry}
217
251
  value={value}
@@ -219,18 +253,27 @@ export default function DockedLayout(props: LayoutProps) {
219
253
  layoutState={layoutState}
220
254
  onPlayerSeek={onPlayerSeek}
221
255
  playerState={playerState}
256
+ docked
222
257
  />
223
258
  )}
224
259
 
225
260
  <View style={styles.content}>
226
- <PlayerImageBackground
227
- configuration={configuration}
228
- entry={entry}
229
- style={backgroundImageStyle(dimensions)}
261
+ <View
262
+ style={backgroundImageStyle(
263
+ dimensions,
264
+ minimisedHeight,
265
+ isActiveGesture
266
+ )}
230
267
  />
231
- <TouchableWithoutFeedback onPress={maximiseVideoModal}>
268
+ <TouchableWithoutFeedback onPress={maximazeVideoModal}>
232
269
  <View
233
- style={[styles.touchableArea, playerDimensionsStyle(dimensions)]}
270
+ style={[
271
+ styles.touchableArea,
272
+ playerDimensionsStyle(
273
+ minimisedHeight,
274
+ isActiveGesture || layoutState.docked
275
+ ),
276
+ ]}
234
277
  >
235
278
  <View
236
279
  style={[
@@ -274,6 +317,6 @@ export default function DockedLayout(props: LayoutProps) {
274
317
  />
275
318
  </View>
276
319
  </View>
277
- </View>
320
+ </AnimationComponent>
278
321
  );
279
322
  }
@@ -19,6 +19,8 @@ const playbackButtonMargins = (
19
19
  ): number =>
20
20
  isLiveShowFullControls ? (isTablet ? 65 : 10) : isTablet ? 95 : 40;
21
21
 
22
+ export const PADDING_HORIZONTAL = 15;
23
+
22
24
  export const playbackButton = ({
23
25
  playerState,
24
26
  liveButton,
@@ -1,6 +1,5 @@
1
1
  import React from "react";
2
2
  import DefaultLayout from "./DefaultLayout";
3
- import DockedLayout from "./DockedLayout";
4
3
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
5
4
 
6
5
  type Dimensions = {
@@ -40,6 +39,7 @@ type LayoutProps = {
40
39
  onSkipLive: () => void;
41
40
  maximiseVideoModal: () => void;
42
41
  dimensions: Dimensions;
42
+ defaultDimensions: Dimensions;
43
43
  playNextData: PlayNextData;
44
44
  title: string | number;
45
45
  summary: string | number;
@@ -48,11 +48,5 @@ type LayoutProps = {
48
48
  export default function Layout(
49
49
  props: LayoutProps
50
50
  ): React.ReactElement<LayoutProps> {
51
- const { layoutState } = props;
52
-
53
- if (layoutState.docked) {
54
- return <DockedLayout {...props} />;
55
- }
56
-
57
51
  return <DefaultLayout {...props} />;
58
52
  }
@@ -11,9 +11,10 @@ type MagicBGProps = {
11
11
  image: string;
12
12
  fallbackUrl?: string;
13
13
  defaultColor: string;
14
+ colors: string[]; // Updated to type string[] as it holds hex values
14
15
  };
15
16
 
16
- const colors = {
17
+ const defaultColors = {
17
18
  overlay: "rgba(0, 0, 0, 0.35)",
18
19
  defaultGradientStart: "rgba(0, 0, 0, 0.25)",
19
20
  defaultGradientEnd: "rgba(0, 0, 0, 0)",
@@ -25,7 +26,7 @@ const styles = StyleSheet.create({
25
26
  },
26
27
  fullScreenOverlay: {
27
28
  ...StyleSheet.absoluteFillObject,
28
- backgroundColor: colors.overlay,
29
+ backgroundColor: defaultColors.overlay,
29
30
  },
30
31
  gradientOverlay: {
31
32
  position: "absolute",
@@ -54,46 +55,63 @@ export const MagicBG: React.FC<MagicBGProps> = ({
54
55
  children,
55
56
  image,
56
57
  fallbackUrl = "",
58
+ colors = [],
57
59
  defaultColor,
58
60
  }) => {
59
61
  const [dominantColor, setDominantColor] =
60
62
  React.useState<string>(defaultColor);
61
63
 
62
64
  const [gradientColors, setGradientColors] = React.useState<string[]>([
63
- colors.defaultGradientStart,
64
- colors.defaultGradientEnd,
65
+ defaultColors.defaultGradientStart,
66
+ defaultColors.defaultGradientEnd,
65
67
  ]);
66
68
 
67
69
  useEffect(() => {
68
70
  const fetchColors = async () => {
69
- if (!image) return;
70
-
71
- try {
72
- const { gradientColors, dominantColor } =
73
- await fetchPaletteAndDominantColor(image);
74
-
75
- setGradientColors(gradientColors);
76
- setDominantColor(dominantColor);
77
- } catch (error) {
71
+ // Check if colors are provided and not empty
72
+ 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
76
+ } else if (image) {
77
+ // If no colors, try fetching from image
78
78
  try {
79
79
  const { gradientColors, dominantColor } =
80
- await fetchPaletteAndDominantColor(fallbackUrl);
80
+ await fetchPaletteAndDominantColor(image);
81
81
 
82
82
  setGradientColors(gradientColors);
83
83
  setDominantColor(dominantColor);
84
- } catch {
85
- setGradientColors([
86
- colors.defaultGradientStart,
87
- colors.defaultGradientEnd,
88
- ]);
89
-
90
- setDominantColor(defaultColor);
84
+ } catch (error) {
85
+ // If image fetch fails, try fallback URL
86
+ try {
87
+ const { gradientColors, dominantColor } =
88
+ await fetchPaletteAndDominantColor(fallbackUrl);
89
+
90
+ setGradientColors(gradientColors);
91
+ setDominantColor(dominantColor);
92
+ } catch {
93
+ // If everything fails, use default color
94
+ setGradientColors([
95
+ defaultColors.defaultGradientStart,
96
+ defaultColors.defaultGradientEnd,
97
+ ]);
98
+
99
+ setDominantColor(defaultColor);
100
+ }
91
101
  }
102
+ } else {
103
+ // If no image or colors, use default color
104
+ setGradientColors([
105
+ defaultColors.defaultGradientStart,
106
+ defaultColors.defaultGradientEnd,
107
+ ]);
108
+
109
+ setDominantColor(defaultColor);
92
110
  }
93
111
  };
94
112
 
95
113
  fetchColors();
96
- }, [image, fallbackUrl, colors, defaultColor]);
114
+ }, []);
97
115
 
98
116
  return (
99
117
  <View style={[styles.container, { backgroundColor: dominantColor }]}>
@@ -36,6 +36,12 @@ import { AudioPlayerWrapper } from "./AudioLayer/AudioPlayerWrapper";
36
36
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
37
37
  import { getAllAccessibilityProps } from "@applicaster/zapp-react-native-utils/configurationUtils";
38
38
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
39
+ import { getTargetScreenDataFromEntry } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useTargetScreenData";
40
+ import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
41
+ import {
42
+ AnimationComponent,
43
+ ComponentAnimationType,
44
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
39
45
 
40
46
  const RNPlayerManager = {
41
47
  QuickBrickPlayerPlugin: isApplePlatform()
@@ -55,6 +61,7 @@ const styles = StyleSheet.create({
55
61
  position: "absolute",
56
62
  zIndex: 1,
57
63
  },
64
+ playerSize: { width: "100%", height: "100%" },
58
65
  });
59
66
 
60
67
  const logger = createLogger({
@@ -555,6 +562,26 @@ export default class VideoPlayer extends React.Component<
555
562
  this.props.listener?.onPlayerDetached?.(nativeEvent);
556
563
  };
557
564
 
565
+ onVideoFullscreenPlayerDidDismiss = () => {
566
+ this.props.listener?.onVideoFullscreenPlayerDidDismiss?.();
567
+ };
568
+
569
+ onVideoFullscreenPlayerDidPresent = () => {
570
+ this.props.listener?.onVideoFullscreenPlayerDidPresent?.();
571
+ };
572
+
573
+ onVideoFullscreenPlayerWillDismiss = () => {
574
+ this.props.listener?.onVideoFullscreenPlayerWillDismiss?.();
575
+ };
576
+
577
+ onVideoFullscreenPlayerWillPresent = () => {
578
+ this.props.listener?.onVideoFullscreenPlayerWillPresent?.();
579
+ };
580
+
581
+ onPlayerClose = () => {
582
+ this.props.listener?.onPlayerClose?.();
583
+ };
584
+
558
585
  onPiPEnter = () => {
559
586
  this.setState({ piped: true });
560
587
  this.props.openPiP();
@@ -758,8 +785,6 @@ export default class VideoPlayer extends React.Component<
758
785
 
759
786
  render() {
760
787
  const {
761
- castAction,
762
- controls,
763
788
  docked,
764
789
  entry,
765
790
  fullscreen,
@@ -769,8 +794,12 @@ export default class VideoPlayer extends React.Component<
769
794
  isTabletPortrait,
770
795
  muted,
771
796
  style,
797
+ castAction,
798
+ controller,
772
799
  } = this.props;
773
800
 
801
+ const supportNativeCast = controller.supportNativeCast();
802
+
774
803
  const { audioTrackId, keyEvent, paused, seek, textTrackId, rate } =
775
804
  this.state;
776
805
 
@@ -817,17 +846,32 @@ export default class VideoPlayer extends React.Component<
817
846
  const liveCatchUpEnabled =
818
847
  this.props.pluginConfiguration?.liveCatchUpEnabled;
819
848
 
820
- const hideNativeControls = isTV()
821
- ? { controls: !this.getPlayNextData() }
822
- : {};
849
+ // TODO: Get screen data more reliable way useCurrentScreenData
850
+ const screenData = getTargetScreenDataFromEntry(this.props.playableItem);
823
851
 
824
- const platformSpecificProps = isApplePlatform()
852
+ const isInlineTV = screenData?.ui_components?.length > 0 && isTV();
853
+
854
+ let platformSpecificProps: any = isApplePlatform()
825
855
  ? {
826
- ...hideNativeControls,
827
856
  resizeMode: nativeResizeMode,
828
857
  }
829
858
  : {};
830
859
 
860
+ const supportsNativeControls =
861
+ !!this.props.controller?.supportsNativeControls?.();
862
+
863
+ const controls = isAppleTV()
864
+ ? supportsNativeControls && !isInlineTV && !this.getPlayNextData()
865
+ : supportsNativeControls && !this.getPlayNextData();
866
+
867
+ const shouldUseNativeControls =
868
+ controls && !docked && !castAction?.state?.connected;
869
+
870
+ platformSpecificProps = {
871
+ ...platformSpecificProps,
872
+ controls: shouldUseNativeControls,
873
+ };
874
+
831
875
  const pictureInPictureEnabled =
832
876
  this.props.pluginConfiguration?.pictureInPictureEnabled;
833
877
 
@@ -835,7 +879,6 @@ export default class VideoPlayer extends React.Component<
835
879
  entry, // Must be passed first in list
836
880
  ...platformSpecificProps,
837
881
  audioTrackId,
838
- controls,
839
882
  docked,
840
883
  enableAutoAudioTrackSelection,
841
884
  fullscreen, // TODO: Check fullscreen params not crash
@@ -890,22 +933,17 @@ export default class VideoPlayer extends React.Component<
890
933
  onVideoEnd: this.onVideoEnd, // complete
891
934
  onVideoError: this.onVideoError, // error
892
935
  onVideoProgress: this.onVideoProgress,
893
- onVideoFullscreenPlayerDidDismiss: this.unHandledEvent(
894
- "onVideoFullscreenPlayerDidPresent"
895
- ),
896
- onVideoFullscreenPlayerDidPresent: this.unHandledEvent(
897
- "onVideoFullscreenPlayerDidPresent"
898
- ),
899
- onVideoFullscreenPlayerWillDismiss: this.unHandledEvent(
900
- "onVideoFullscreenPlayerWillDismiss"
901
- ),
902
- onVideoFullscreenPlayerWillPresent: this.unHandledEvent(
903
- "onVideoFullscreenPlayerWillPresent"
904
- ),
936
+ onVideoFullscreenPlayerDidDismiss: this.onVideoFullscreenPlayerDidDismiss,
937
+ onVideoFullscreenPlayerDidPresent: this.onVideoFullscreenPlayerDidPresent,
938
+ onVideoFullscreenPlayerWillDismiss:
939
+ this.onVideoFullscreenPlayerWillDismiss,
940
+ onVideoFullscreenPlayerWillPresent:
941
+ this.onVideoFullscreenPlayerWillPresent,
905
942
  onVideoLoad: this.onVideoLoad,
906
943
  onVideoLoadStart: this.unHandledEvent("onVideoLoadStart"),
907
944
  onVideoBuffer: this.onVideoBuffer,
908
945
  onPlaybackStalled: this.onPlaybackStalled,
946
+ onPlayerClose: this.onPlayerClose,
909
947
  onPlayerAttached: this.onPlayerAttached,
910
948
  onPlayerDetached: this.onPlayerDetached,
911
949
  };
@@ -979,7 +1017,7 @@ export default class VideoPlayer extends React.Component<
979
1017
  !needToShowPlayNextOverlay &&
980
1018
  this.props.mode !== "PIP";
981
1019
 
982
- if (controls === true) {
1020
+ if (shouldUseNativeControls) {
983
1021
  return null;
984
1022
  }
985
1023
 
@@ -1011,10 +1049,12 @@ export default class VideoPlayer extends React.Component<
1011
1049
  inline,
1012
1050
  docked,
1013
1051
  isModal,
1052
+ pip: this.state.piped,
1014
1053
  layoutState: {
1015
1054
  inline,
1016
1055
  docked,
1017
1056
  isModal,
1057
+ pip: this.state.piped,
1018
1058
  },
1019
1059
  configuration: this.getPluginConfiguration(),
1020
1060
  fullscreen: fullscreen,
@@ -1030,29 +1070,14 @@ export default class VideoPlayer extends React.Component<
1030
1070
  const renderMobilePlayer = () => {
1031
1071
  return isFullScreenAudioPlayer && isAudioItem ? (
1032
1072
  <View style={containerStyle}>
1033
- {!castAction?.state?.connected && (
1073
+ {(!castAction?.state?.connected || supportNativeCast) && (
1034
1074
  <this.props.PlayerComponent
1035
1075
  ref={this._assignRoot}
1036
1076
  {...nativeEvents}
1037
1077
  {...nativeProps}
1038
1078
  />
1039
1079
  )}
1040
- <AudioPlayerWrapper {...sharedWrapperViewProps}>
1041
- <View>
1042
- {
1043
- <PlayNextOverlay
1044
- item={entry}
1045
- playerIsDocked={docked}
1046
- pluginConfiguration={this.getPluginConfiguration()}
1047
- playerId={this.props.playerId}
1048
- playNextData={this.getPlayNextData()}
1049
- setNextVideoPreloadThresholdPercentage={
1050
- this.props.setNextVideoPreloadThresholdPercentage
1051
- }
1052
- />
1053
- }
1054
- </View>
1055
- </AudioPlayerWrapper>
1080
+ <AudioPlayerWrapper {...sharedWrapperViewProps} />
1056
1081
  </View>
1057
1082
  ) : (
1058
1083
  <PlayerWrapper {...sharedWrapperViewProps}>
@@ -1067,11 +1092,24 @@ export default class VideoPlayer extends React.Component<
1067
1092
  <View style={styles.container}>
1068
1093
  <View style={[playerContainerStyles, playerDimensions]}>
1069
1094
  {isAudioItem && (
1070
- <AudioPlayer
1071
- style={playerDimensions}
1072
- audio_item={entry}
1073
- plugin_configuration={this.getPluginConfiguration()}
1074
- />
1095
+ <AnimationComponent
1096
+ animationType={ComponentAnimationType.player}
1097
+ additionalData={{
1098
+ ...playerDimensions,
1099
+ inlineAudioPlayer: true,
1100
+ }}
1101
+ style={getStyle(playerDimensions)}
1102
+ >
1103
+ <AudioPlayer
1104
+ style={
1105
+ isTV()
1106
+ ? getStyle(playerDimensions)
1107
+ : styles.playerSize
1108
+ }
1109
+ audio_item={entry}
1110
+ plugin_configuration={this.getPluginConfiguration()}
1111
+ />
1112
+ </AnimationComponent>
1075
1113
  )}
1076
1114
 
1077
1115
  {shouldRenderBackground && (
@@ -1083,17 +1121,28 @@ export default class VideoPlayer extends React.Component<
1083
1121
  />
1084
1122
  )}
1085
1123
 
1086
- {!castAction?.state?.connected && (
1087
- <this.props.PlayerComponent
1088
- ref={this._assignRoot}
1089
- {...nativeEvents}
1090
- {...nativeProps}
1091
- listener={this.props.listener}
1124
+ {(!castAction?.state?.connected || supportNativeCast) && (
1125
+ <AnimationComponent
1126
+ animationType={ComponentAnimationType.player}
1127
+ additionalData={playerDimensions}
1092
1128
  style={getStyle(playerDimensions)}
1093
- pluginConfiguration={this.getPluginConfiguration()}
1094
1129
  >
1095
- {this.childrenWithPlayNext()}
1096
- </this.props.PlayerComponent>
1130
+ <this.props.PlayerComponent
1131
+ ref={this._assignRoot}
1132
+ {...nativeEvents}
1133
+ {...nativeProps}
1134
+ listener={this.props.listener}
1135
+ style={
1136
+ isTV()
1137
+ ? getStyle(playerDimensions)
1138
+ : styles.playerSize
1139
+ }
1140
+ pluginConfiguration={this.getPluginConfiguration()}
1141
+ controller={this.props.controller}
1142
+ >
1143
+ {this.childrenWithPlayNext()}
1144
+ </this.props.PlayerComponent>
1145
+ </AnimationComponent>
1097
1146
  )}
1098
1147
  </View>
1099
1148
 
@@ -1127,6 +1176,7 @@ export default class VideoPlayer extends React.Component<
1127
1176
  listener={this.props.listener}
1128
1177
  style={this.takeParentStyle}
1129
1178
  pluginConfiguration={this.getPluginConfiguration()}
1179
+ controller={this.props.controller}
1130
1180
  >
1131
1181
  {this.childrenWithPlayNext()}
1132
1182
  </this.props.PlayerComponent>