@applicaster/quick-brick-player 13.0.0-rc.12 → 13.0.0-rc.121

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/package.json +27 -14
  2. package/src/CellPlayer/Assets/AssetsComponent.tsx +17 -3
  3. package/src/Player/AudioLayer/AudioDescription.tsx +107 -68
  4. package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +205 -281
  5. package/src/Player/AudioLayer/Layout/Controls/VerticalGutter.tsx +45 -0
  6. package/src/Player/AudioLayer/Layout/Controls/index.tsx +320 -0
  7. package/src/Player/AudioLayer/Layout/{defaultLayoutsConts.ts → Controls/utils.ts} +7 -25
  8. package/src/Player/AudioLayer/Layout/Debug/index.tsx +55 -0
  9. package/src/Player/AudioLayer/Layout/{DockedLayout.tsx → DockedControls/index.tsx} +77 -81
  10. package/src/Player/AudioLayer/Layout/MobileLayout.tsx +230 -0
  11. package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +70 -0
  12. package/src/Player/AudioLayer/Layout/PlayerImage/FlexImage.tsx +20 -0
  13. package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +108 -0
  14. package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +36 -0
  15. package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +220 -0
  16. package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +223 -0
  17. package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +128 -0
  18. package/src/Player/AudioLayer/Layout/VerticalGutter/index.tsx +8 -0
  19. package/src/Player/AudioLayer/Layout/const.ts +26 -0
  20. package/src/Player/AudioLayer/Layout/debug.ts +6 -0
  21. package/src/Player/AudioLayer/Layout/hooks/index.ts +5 -0
  22. package/src/Player/AudioLayer/Layout/hooks/useMobileLayoutImageSize.ts +14 -0
  23. package/src/Player/AudioLayer/Layout/hooks/useTabletLayoutImageSize.ts +105 -0
  24. package/src/Player/AudioLayer/Layout/hooks/useTransportInfoHeight.ts +54 -0
  25. package/src/Player/AudioLayer/Layout/index.tsx +21 -48
  26. package/src/Player/AudioLayer/Layout/types.ts +7 -0
  27. package/src/Player/AudioLayer/Layout/utils.ts +7 -0
  28. package/src/Player/AudioLayer/SleepTimerManager.ts +119 -0
  29. package/src/Player/AudioLayer/SpeedManager.ts +52 -0
  30. package/src/Player/AudioLayer/utils.ts +27 -0
  31. package/src/Player/Const/sleepTimerOptions.ts +39 -0
  32. package/src/Player/Const/speedOptions.ts +4 -0
  33. package/src/Player/Utils/Utils.ts +0 -5
  34. package/src/Player/Utils/index.tsx +117 -0
  35. package/src/Player/hooks/useIsCasting.tsx +57 -0
  36. package/src/Player/index.tsx +39 -22
  37. package/src/QuickBrickPlayer.tsx +3 -0
  38. package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +0 -452
  39. package/src/Player/AudioLayer/LinearGradient/LinearGradientProxy.ts +0 -3
  40. package/src/Player/AudioLayer/LinearGradient/LinearGradientProxy.web.ts +0 -3
  41. package/src/Player/AudioLayer/LinearGradient/index.ts +0 -1
  42. package/src/Player/AudioLayer/MagicBG/index.tsx +0 -155
  43. package/src/Player/Helpers/VideoResizeMode.ts +0 -7
  44. package/src/utils/utils.ts +0 -25
@@ -1,7 +1,13 @@
1
1
  import * as React from "react";
2
- import { ViewStyle, View } from "react-native";
2
+ import { useEffect, useState } from "react";
3
+ import {
4
+ Dimensions as ScreenDimensions,
5
+ StyleSheet,
6
+ View,
7
+ ViewStyle,
8
+ } from "react-native";
3
9
 
4
- import { SafeAreaView, Edge } from "react-native-safe-area-context";
10
+ import { Edge, SafeAreaView } from "react-native-safe-area-context";
5
11
  import { usePlayerState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerState";
6
12
  import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
7
13
  import * as R from "ramda";
@@ -15,24 +21,35 @@ import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
15
21
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
16
22
  import { PlayerDetails } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/PlayerDetails";
17
23
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
18
- import { useEffect, useState } from "react";
19
- import { MagicBG } from "./MagicBG";
24
+ import { formatSpeed } from "./utils";
20
25
  import {
21
- formatSpeed,
22
- hasDatePassed,
23
- loadItemsFromStorage,
24
- NAMESPACE,
25
- } from "./utils";
26
- import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
27
- import { addQueryParam, getLiveUrl } from "../Utils";
26
+ getBottomSheetModalDefaultItemIcon,
27
+ getBottomSheetModalIconProps,
28
+ getBottomSheetModalItemProps,
29
+ getBottomSheetModalLabelProps,
30
+ getBottomSheetModalSelectedItemIcon,
31
+ } from "../Utils";
28
32
  import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
29
33
  import {
34
+ AnimatedScrollModal,
30
35
  AnimationComponent,
31
36
  ComponentAnimationType,
32
- AnimatedScrollModal,
33
37
  } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
34
- import { PADDING_HORIZONTAL } from "./Layout/defaultLayoutsConts";
38
+ import { PADDING_HORIZONTAL } from "./Layout/const";
35
39
  import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
40
+ import {
41
+ SLEEP_TIMER_OFF,
42
+ SleepTimerController,
43
+ SleepTimerState,
44
+ } from "./SleepTimerManager";
45
+ import { DEFAULT_SPEED, PlaybackSpeed, SpeedController } from "./SpeedManager";
46
+ import {
47
+ dismissModal as dismissBottomSheetModal,
48
+ openBottomSheetModal,
49
+ useModalStoreState,
50
+ } from "@applicaster/zapp-react-native-utils/modalState";
51
+ import { getSleepTimerOptions } from "../Const/sleepTimerOptions";
52
+ import { speedOptions } from "../Const/speedOptions";
36
53
 
37
54
  const { log_debug } = createLogger({
38
55
  subsystem: "UIComponent",
@@ -48,8 +65,7 @@ type Dimensions = {
48
65
 
49
66
  type Configuration = {
50
67
  [key: string]: any;
51
- magic_background?: boolean;
52
- audio_player_background_image_query?: string;
68
+ audio_player_background_image_default_color?: string;
53
69
  tablet_landscape_sidebar_width?: string;
54
70
  tablet_landscape_player_container_background_color?: string;
55
71
  };
@@ -57,7 +73,6 @@ type Configuration = {
57
73
  type Props = {
58
74
  children: React.ReactNode;
59
75
  inline?: boolean;
60
- docked?: boolean;
61
76
  configuration: Configuration;
62
77
  style: ViewStyle & Dimensions;
63
78
  entry: ZappEntry;
@@ -71,57 +86,29 @@ type Props = {
71
86
  isTabletPortrait?: boolean;
72
87
  };
73
88
 
74
- export const defaultAspectRatioWidth = (height: number): number =>
75
- (height / 9) * 16;
76
-
77
- const Styles = {
78
- mainContainer: (style: ViewStyle) => {
79
- const combinedStyle: ViewStyle = {
80
- flex: 1,
81
- };
89
+ const { width: SCREEN_WIDTH } = ScreenDimensions.get("screen");
82
90
 
83
- return {
84
- ...combinedStyle,
85
- ...style,
86
- };
87
- },
91
+ const Styles = StyleSheet.create({
88
92
  playerContainer: {
89
93
  flex: 1,
90
94
  },
91
95
  paddingHorizontal: { paddingTop: 0, paddingHorizontal: PADDING_HORIZONTAL },
92
-
93
- controlsMobile: {
94
- height: 50,
95
- marginHorizontal: 24,
96
- },
97
- controlsTablet: {
98
- height: 96,
99
- marginHorizontal: 24,
100
- },
101
- playerDetails: { marginTop: 60 },
102
-
103
- linearGradient: {
104
- flex: 1,
96
+ playerDetails: {
97
+ marginTop: 60,
98
+ alignSelf: "center",
99
+ width: SCREEN_WIDTH - PADDING_HORIZONTAL * 2,
105
100
  },
106
- };
101
+ });
107
102
 
108
- const sleeps = [
109
- // { label: "End of episode", value: "episode" },
110
- { label: "5 minutes", value: 5 },
111
- { label: "10 minutes", value: 10 },
112
- { label: "15 minutes", value: 15 },
113
- { label: "30 minutes", value: 30 },
114
- { label: "45 minutes", value: 45 },
115
- { label: "1 hour", value: 60 },
116
- { label: "2 hours", value: 120 },
117
- ];
103
+ const edges: Edge[] = ["right", "left", "top"];
118
104
 
119
105
  export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
120
106
  const player = usePlayer();
121
107
  const playerState = usePlayerState("audio-player-wrapper");
122
108
 
109
+ const modalBottomSheetState = useModalStoreState();
110
+
123
111
  const {
124
- docked,
125
112
  configuration,
126
113
  style,
127
114
  entry,
@@ -131,40 +118,64 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
131
118
  isTabletPortrait,
132
119
  } = props;
133
120
 
134
- const speeds = [
135
- { asset: configuration["speed_0_8"], value: 0.8 },
136
- { asset: configuration["speed_1"], value: 1.0 },
137
- { asset: configuration["speed_1_2"], value: 1.2 },
138
- { asset: configuration["speed_1_5"], value: 1.5 },
139
- { asset: configuration["speed_2"], value: 2.0 },
140
- ];
141
-
142
121
  const isTablet = useIsTablet();
143
122
 
144
123
  const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
145
124
 
146
- // TODO: Have generic place for live url from configuration. Currently its hardcoded for client.
147
-
148
- const feedUrl = getLiveUrl(entry?.extensions?.live?.channel_src ?? "");
149
-
150
125
  const { title, summary } = useTitleSummaryOverlay(
151
- feedUrl,
152
126
  entry,
153
127
  playerState?.isLive,
154
128
  configuration?.audio_live_player_update_interval
155
129
  );
156
130
 
157
- const [playbackSpeed, setPlaybackSpeed] = useState({
158
- asset: configuration["speed_1"],
159
- value: 1.0,
160
- });
131
+ const [sleepTimerManager, setSleepTimerManager] =
132
+ useState<SleepTimerController | null>(null);
161
133
 
162
- const [sleepTimer, setSleepTimer] = useState({
163
- label: "playback_sleep_off_label",
164
- value: 0,
165
- });
134
+ const [speedManager, setSpeedManager] = useState<SpeedController | null>(
135
+ null
136
+ );
137
+
138
+ useEffect(() => {
139
+ if (player) {
140
+ setSleepTimerManager(new SleepTimerController(player));
141
+ setSpeedManager(new SpeedController(player));
142
+ } else {
143
+ setSleepTimerManager(null);
144
+ setSpeedManager(null);
145
+ }
146
+ }, [player]);
147
+
148
+ useEffect(() => {
149
+ if (!player) return;
150
+
151
+ const unsubscribe = player.addListener({
152
+ id: "audio-player-wrapper-bottom-sheet-modal",
153
+ listener: {
154
+ onVideoEnd: () => {
155
+ // Dismiss the bottom sheet modal when the video ends to avoid audio breaks and app freeze
156
+ if (modalBottomSheetState.visible) {
157
+ dismissBottomSheetModal();
158
+ }
159
+ },
160
+ },
161
+ });
166
162
 
167
- const [sleepTimerDate, setSleepTimerDate] = useState(0);
163
+ return () => unsubscribe?.();
164
+ }, [player, modalBottomSheetState.visible, sleepTimerManager]);
165
+
166
+ React.useEffect(() => {
167
+ if (playerState?.isLive) {
168
+ // Resetting a sleep timer when audio content is live (Timer not supported on live video)
169
+ sleepTimerManager.setSleepTimer(SLEEP_TIMER_OFF).catch();
170
+ }
171
+ }, [playerState?.isLive]);
172
+
173
+ const [playbackSpeed, setPlaybackSpeed] =
174
+ useState<PlaybackSpeed>(DEFAULT_SPEED);
175
+
176
+ const [sleepTimerState, setSleepTimerState] = useState<SleepTimerState>(
177
+ new SleepTimerState(SLEEP_TIMER_OFF, 0)
178
+ );
168
179
 
169
180
  const [defaultDimensions, setDefaultDimensions] =
170
181
  React.useState<Dimensions>(undefined);
@@ -174,8 +185,6 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
174
185
  height: 0,
175
186
  });
176
187
 
177
- const navigator = useNavigation();
178
-
179
188
  const { maximiseVideoModal } = useNavigation();
180
189
 
181
190
  const onLayout = (event: any) => {
@@ -189,82 +198,27 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
189
198
  if (!defaultDimensions) setDefaultDimensions({ width, height });
190
199
  };
191
200
 
192
- const cancelSleepTimer = async () => {
193
- player?.cancelSleepTimer();
194
-
195
- setSleepTimer({
196
- label: "playback_sleep_off_label",
197
- value: 0,
198
- });
199
-
200
- setSleepTimerDate(0);
201
-
202
- await localStorage.removeItem("sleep", NAMESPACE);
203
- };
204
-
205
- const startSleepTimer = (timerValue: number) => {
206
- try {
207
- const currentDate = new Date();
208
- const futureDate = new Date(currentDate.getTime() + timerValue * 60000);
209
- const futureSleepDate = futureDate.getTime();
210
-
211
- player?.cancelSleepTimer();
212
-
213
- player?.startSleepTimer(futureSleepDate);
214
-
215
- return futureSleepDate;
216
- } catch (e) {
217
- return 0;
218
- }
219
- };
220
-
221
- const handleSleep = async (
222
- timerValue: number,
223
- currentFutureSleepDate = null
224
- ) => {
225
- if (currentFutureSleepDate) {
226
- if (hasDatePassed(currentFutureSleepDate)) {
227
- await cancelSleepTimer();
228
-
229
- return 0;
230
- }
231
-
232
- return currentFutureSleepDate;
233
- }
234
-
235
- if (timerValue === 0) {
236
- await cancelSleepTimer();
237
-
238
- return timerValue;
239
- }
240
-
241
- return startSleepTimer(timerValue);
242
- };
243
-
244
201
  useEffect(() => {
245
- const loadSpeed = async () => {
246
- const speed = await loadItemsFromStorage("speed");
202
+ const sleepSubscription =
203
+ sleepTimerManager?.sleepTimerState$.subscribe(setSleepTimerState);
247
204
 
248
- if (playerState.isReadyToPlay && speed) {
249
- setPlaybackSpeed(speed);
250
- player?.setPlaybackRate(speed.value);
251
- }
252
- };
253
-
254
- const loadSleep = async () => {
255
- const sleep = await loadItemsFromStorage("sleep");
205
+ const speedSubscription =
206
+ speedManager?.playbackSpeed$.subscribe(setPlaybackSpeed);
256
207
 
257
- if (playerState.isReadyToPlay && sleep) {
258
- const { futureSleepDate, ...rest } = sleep;
259
- setSleepTimer(rest);
260
- const sleepTimerDate = await handleSleep(sleep.value, futureSleepDate);
261
- setSleepTimerDate(sleepTimerDate);
262
- }
208
+ return () => {
209
+ sleepSubscription?.unsubscribe();
210
+ speedSubscription?.unsubscribe();
263
211
  };
212
+ }, [sleepTimerManager, speedManager]);
213
+
214
+ useEffect(() => {
215
+ if (!playerState.isReadyToPlay) {
216
+ return;
217
+ }
264
218
 
265
- loadSpeed().then();
266
- loadSleep().then();
267
- }, [playerState.isReadyToPlay]);
219
+ void speedManager?.loadSpeed();
220
+ void sleepTimerManager?.loadSleep();
221
+ }, [playerState.isReadyToPlay, speedManager, sleepTimerManager]);
268
222
 
269
223
  const getValue = (key: string, stylesObj: {}) =>
270
224
  R.propOr(null, key, stylesObj);
@@ -292,7 +246,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
292
246
 
293
247
  const onPlaybackButtonPress = React.useCallback(() => {
294
248
  if (playerState.isBuffering) return;
295
- playerState.isPaused ? player?.play() : player.pause();
249
+ playerState.isPaused ? player?.play() : player?.pause();
296
250
  }, [playerState, player]);
297
251
 
298
252
  const seekDuration = toNumberWithDefault(10, value("seek_duration"));
@@ -307,57 +261,77 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
307
261
 
308
262
  const onSpeedSelect = React.useCallback(
309
263
  async (item) => {
310
- setPlaybackSpeed(item);
311
- player.setPlaybackRate(item.value);
312
- await localStorage.setItem("speed", item, NAMESPACE);
264
+ await speedManager?.setPlaybackSpeed(item);
313
265
  },
314
- [player, setPlaybackSpeed]
266
+ [speedManager]
315
267
  );
316
268
 
317
269
  const onSleepSelect = React.useCallback(
318
270
  async (item) => {
319
- setSleepTimer(item);
320
- const futureSleepDate = await handleSleep(item.value);
321
- setSleepTimerDate(futureSleepDate);
322
-
323
- await localStorage.setItem(
324
- "sleep",
325
- { ...item, futureSleepDate },
326
- NAMESPACE
327
- );
271
+ await sleepTimerManager?.setSleepTimer(item);
328
272
  },
329
- [player, setSleepTimer]
273
+ [sleepTimerManager]
330
274
  );
331
275
 
332
276
  const onSpeedButtonPress = React.useCallback(() => {
333
- navigator.openModalBottomSheet({
277
+ openBottomSheetModal({
334
278
  modalBottomSheetContentProps: {
335
- items: speeds,
279
+ items: speedOptions,
336
280
  current_selection: playbackSpeed,
337
281
  onPress: onSpeedSelect,
338
282
  title: configuration["playback_speed_title"],
283
+ itemProps: getBottomSheetModalItemProps,
284
+ iconProps: getBottomSheetModalIconProps,
285
+ labelProps: getBottomSheetModalLabelProps,
286
+ getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
287
+ getDefaultItemIcon: getBottomSheetModalDefaultItemIcon,
288
+ iconPlacement: "right",
339
289
  },
340
290
  options: {
341
- supportedOrientations: ["portrait"],
291
+ supportedOrientations: [
292
+ "portrait",
293
+ "landscape",
294
+ "portrait-upside-down",
295
+ ],
342
296
  },
343
297
  });
344
- }, [playbackSpeed, setPlaybackSpeed]);
298
+ }, [playbackSpeed, onSpeedSelect, configuration["playback_speed_title"]]);
345
299
 
346
300
  const onSleepButtonPress = React.useCallback(() => {
347
- navigator.openModalBottomSheet({
301
+ openBottomSheetModal({
348
302
  modalBottomSheetContentProps: {
349
- items: [
350
- { label: configuration["playback_sleep_off_label"], value: 0 },
351
- ].concat(sleeps),
352
- current_selection: sleepTimer,
303
+ items: getSleepTimerOptions(configuration),
304
+ current_selection: sleepTimerState.timer,
353
305
  onPress: onSleepSelect,
354
306
  title: configuration["playback_sleep_title"],
307
+ itemProps: getBottomSheetModalItemProps,
308
+ iconProps: getBottomSheetModalIconProps,
309
+ labelProps: getBottomSheetModalLabelProps,
310
+ getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
311
+ iconPlacement: "right",
355
312
  },
356
313
  options: {
357
- supportedOrientations: ["portrait"],
314
+ supportedOrientations: [
315
+ "portrait",
316
+ "landscape",
317
+ "portrait-upside-down",
318
+ ],
358
319
  },
359
320
  });
360
- }, [sleepTimer, setSleepTimer, configuration]);
321
+ }, [
322
+ sleepTimerState.timer,
323
+ configuration["playback_sleep_title"],
324
+ configuration["playback_sleep_off_label"],
325
+ onSleepSelect,
326
+ ]);
327
+
328
+ // This method does not actually cancel the sleep timer
329
+ // (and should not to avoid race condition),
330
+ // it just performs the cleanup when timer expires
331
+ // TODO: rename it in code above
332
+ const cancelSleepTimer = React.useCallback(() => {
333
+ sleepTimerManager?.onTimerExpired();
334
+ }, [sleepTimerManager]);
361
335
 
362
336
  const onCloseVideoModal = React.useCallback(() => {
363
337
  playerManager.close();
@@ -378,111 +352,61 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
378
352
  player?.seekTo(0);
379
353
  }, [player]);
380
354
 
381
- const edges = React.useMemo<Edge[]>(() => ["right", "left", "top"], [docked]);
382
-
383
- const ContainerView = SafeAreaView;
384
-
385
- const MainContent = React.useMemo(() => {
386
- return (
387
- <ContainerView style={Styles.mainContainer(style)} edges={edges}>
388
- <AnimationComponent
389
- animationType={ComponentAnimationType.moveUpComponent}
390
- additionalData={{ saveArea: true }}
391
- style={Styles.playerContainer}
392
- >
393
- <AnimatedScrollModal>
394
- <View style={Styles.playerContainer} onLayout={onLayout}>
395
- <Layout
396
- entry={entry}
397
- playerState={playerState}
398
- layoutState={layoutState}
399
- value={value}
400
- onPlaybackButtonPress={onPlaybackButtonPress}
401
- onRewindButtonPress={onRewindButtonPress}
402
- onForwardButtonPress={onForwardButtonPress}
403
- onPlayerSeek={onPlayerSeek}
404
- onSkipStart={onSkipStart}
405
- onSpeedButtonPress={onSpeedButtonPress}
406
- onSleepButtonPress={onSleepButtonPress}
407
- playbackSpeedName={formatSpeed(playbackSpeed.value)}
408
- playbackSleepName={
409
- sleepTimer.value === 0 ? "sleep_timer" : "sleep_timer_active"
410
- }
411
- sleepTimerDate={sleepTimerDate}
412
- cancelSleepTimer={cancelSleepTimer}
413
- onSkipLive={onSkipLive}
414
- onCloseVideoModal={onCloseVideoModal}
415
- maximiseVideoModal={maximiseVideoModal}
416
- configuration={configuration}
417
- dimensions={dimensions}
418
- playNextData={playNextData}
419
- title={title}
420
- summary={summary}
421
- defaultDimensions={defaultDimensions}
422
- />
423
- <View style={Styles.paddingHorizontal}>
424
- {children}
425
- <AnimationComponent
426
- animationType={ComponentAnimationType.componentFade}
427
- >
428
- <PlayerDetails
429
- configuration={configuration}
430
- style={Styles.playerDetails}
431
- entry={entry}
432
- isTabletLandscape={isTabletLandscape}
433
- isAudioPlayer={true}
434
- />
435
- </AnimationComponent>
436
- </View>
437
- </View>
438
- </AnimatedScrollModal>
439
- </AnimationComponent>
440
- </ContainerView>
441
- );
442
- }, [
443
- docked,
444
- style,
445
- entry,
446
- playerState,
447
- layoutState,
448
- value,
449
- onPlaybackButtonPress,
450
- onRewindButtonPress,
451
- onForwardButtonPress,
452
- onPlayerSeek,
453
- onCloseVideoModal,
454
- maximiseVideoModal,
455
- configuration,
456
- dimensions,
457
- children,
458
- playNextData,
459
- onSkipStart,
460
- onSkipLive,
461
- sleepTimer.value,
462
- sleepTimerDate,
463
- ]);
464
-
465
- if (configuration.magic_background) {
466
- const baseUrl = entry?.extensions?.audio_player_background_image ?? "";
467
-
468
- const queryParamName =
469
- configuration.audio_player_background_image_query || "width";
470
-
471
- const queryParamValue = 15;
472
-
473
- const url = addQueryParam(baseUrl, queryParamName, queryParamValue);
474
-
475
- return (
476
- <MagicBG
477
- image={url}
478
- fallbackUrl={baseUrl}
479
- colors={entry?.extensions?.backgroundColors}
480
- defaultColor="#000000"
355
+ return (
356
+ <SafeAreaView style={[style, Styles.playerContainer]} edges={edges}>
357
+ <AnimationComponent
358
+ animationType={ComponentAnimationType.moveUpComponent}
359
+ additionalData={{ saveArea: true }}
360
+ style={Styles.playerContainer}
481
361
  >
482
- {MainContent}
483
- </MagicBG>
484
- );
485
- }
486
-
487
- return MainContent;
362
+ <AnimatedScrollModal>
363
+ <View style={Styles.playerContainer} onLayout={onLayout}>
364
+ <Layout
365
+ entry={entry}
366
+ playerState={playerState}
367
+ layoutState={layoutState}
368
+ value={value}
369
+ onPlaybackButtonPress={onPlaybackButtonPress}
370
+ onRewindButtonPress={onRewindButtonPress}
371
+ onForwardButtonPress={onForwardButtonPress}
372
+ onPlayerSeek={onPlayerSeek}
373
+ onSkipStart={onSkipStart}
374
+ onSpeedButtonPress={onSpeedButtonPress}
375
+ onSleepButtonPress={onSleepButtonPress}
376
+ playbackSpeedName={formatSpeed(playbackSpeed.value)}
377
+ playbackSleepName={
378
+ sleepTimerState.timer.value === 0
379
+ ? "sleep_timer"
380
+ : "sleep_timer_active"
381
+ }
382
+ sleepTimerDate={sleepTimerState.timerDate}
383
+ cancelSleepTimer={cancelSleepTimer}
384
+ onSkipLive={onSkipLive}
385
+ onCloseVideoModal={onCloseVideoModal}
386
+ maximiseVideoModal={maximiseVideoModal}
387
+ configuration={configuration}
388
+ dimensions={dimensions}
389
+ playNextData={playNextData}
390
+ title={title}
391
+ summary={summary}
392
+ />
393
+ <View style={Styles.paddingHorizontal}>
394
+ {children}
395
+ <AnimationComponent
396
+ animationType={ComponentAnimationType.componentFade}
397
+ >
398
+ <PlayerDetails
399
+ isAudioPlayer
400
+ configuration={configuration}
401
+ style={Styles.playerDetails}
402
+ entry={entry}
403
+ isTabletLandscape={isTabletLandscape}
404
+ />
405
+ </AnimationComponent>
406
+ </View>
407
+ </View>
408
+ </AnimatedScrollModal>
409
+ </AnimationComponent>
410
+ </SafeAreaView>
411
+ );
488
412
  };
@@ -0,0 +1,45 @@
1
+ import * as React from "react";
2
+ import { View, StyleSheet } from "react-native";
3
+
4
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
5
+
6
+ import {
7
+ TABLET_VERTICAL_SEPARATOR_HEIGHT,
8
+ TABLET_VERTICAL_SEPARATOR_HEIGHT_S,
9
+ } from "../const";
10
+
11
+ import { withDebug } from "../debug";
12
+ import { useSafeAreaFrame } from "react-native-safe-area-context";
13
+ import { isSmallTabletHeight } from "../utils";
14
+
15
+ const styles = StyleSheet.create({
16
+ flexingHeight: {
17
+ flex: 1,
18
+ ...withDebug({ backgroundColor: "red" }),
19
+ },
20
+ fixedHeight: {
21
+ height: TABLET_VERTICAL_SEPARATOR_HEIGHT,
22
+ ...withDebug({ backgroundColor: "red" }),
23
+ },
24
+ fixedHeightSmall: {
25
+ height: TABLET_VERTICAL_SEPARATOR_HEIGHT_S,
26
+ ...withDebug({ backgroundColor: "red" }),
27
+ },
28
+ });
29
+
30
+ export const VerticalGutter = () => {
31
+ const isTablet = useIsTablet();
32
+ const { height } = useSafeAreaFrame();
33
+
34
+ return (
35
+ <View
36
+ style={
37
+ isTablet
38
+ ? isSmallTabletHeight(height)
39
+ ? styles.fixedHeightSmall
40
+ : styles.fixedHeight
41
+ : styles.flexingHeight
42
+ }
43
+ />
44
+ );
45
+ };