@applicaster/quick-brick-player 13.0.0-alpha.3592913150 → 13.0.0-alpha.3637360334

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 (30) hide show
  1. package/package.json +8 -12
  2. package/src/CellPlayer/Assets/AssetsComponent.tsx +17 -3
  3. package/src/Player/AudioLayer/AudioDescription.tsx +6 -4
  4. package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +118 -129
  5. package/src/Player/AudioLayer/Layout/Controls/VerticalGutter.tsx +26 -0
  6. package/src/Player/AudioLayer/Layout/Controls/index.tsx +307 -0
  7. package/src/Player/AudioLayer/Layout/{defaultLayoutsConts.ts → Controls/utils.ts} +5 -13
  8. package/src/Player/AudioLayer/Layout/{DockedLayout.tsx → DockedControls/index.tsx} +43 -66
  9. package/src/Player/AudioLayer/Layout/MobileLayout.tsx +209 -0
  10. package/src/Player/AudioLayer/Layout/PlayerImage/ScaledImage.tsx +83 -0
  11. package/src/Player/AudioLayer/Layout/PlayerImage/StaticImage.tsx +32 -0
  12. package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +101 -0
  13. package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +17 -0
  14. package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +206 -0
  15. package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +218 -0
  16. package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +103 -0
  17. package/src/Player/AudioLayer/Layout/VerticalGutter/index.tsx +8 -0
  18. package/src/Player/AudioLayer/Layout/const.ts +19 -0
  19. package/src/Player/AudioLayer/Layout/debug.ts +5 -0
  20. package/src/Player/AudioLayer/Layout/hooks/index.ts +3 -0
  21. package/src/Player/AudioLayer/Layout/hooks/useImageSize.ts +99 -0
  22. package/src/Player/AudioLayer/Layout/hooks/useTransportInfoHeight.ts +54 -0
  23. package/src/Player/AudioLayer/Layout/index.tsx +34 -30
  24. package/src/Player/AudioLayer/Layout/types.ts +49 -0
  25. package/src/Player/AudioLayer/MagicBG/index.tsx +1 -1
  26. package/src/Player/AudioLayer/SleepTimerManager.ts +119 -0
  27. package/src/Player/AudioLayer/SpeedManager.ts +52 -0
  28. package/src/Player/AudioLayer/utils.ts +24 -0
  29. package/src/Player/index.tsx +27 -18
  30. package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +0 -452
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@applicaster/quick-brick-player",
3
- "version": "13.0.0-alpha.3592913150",
3
+ "version": "13.0.0-alpha.3637360334",
4
4
  "description": "Quick Brick Player",
5
5
  "main": "./src/index.ts",
6
6
  "types": "index.d.ts",
7
7
  "scripts": {
8
- "test": "jest -w=2",
9
8
  "build": "echo 'skipping ts build for core packages'"
10
9
  },
11
10
  "files": [
@@ -36,11 +35,11 @@
36
35
  },
37
36
  "homepage": "https://github.com/applicaster/quickbrick#readme",
38
37
  "dependencies": {
39
- "@applicaster/quick-brick-mobile-transport-controls": "13.0.0-alpha.3592913150",
40
- "@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.19",
41
- "@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.3592913150",
42
- "@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.3592913150",
43
- "@applicaster/zapp-react-native-utils": "13.0.0-alpha.3592913150",
38
+ "@applicaster/quick-brick-mobile-transport-controls": "13.0.0-alpha.3637360334",
39
+ "@applicaster/quick-brick-tv-transport-controls": "13.0.0-rc.56",
40
+ "@applicaster/zapp-react-native-tvos-app": "13.0.0-alpha.3637360334",
41
+ "@applicaster/zapp-react-native-ui-components": "13.0.0-alpha.3637360334",
42
+ "@applicaster/zapp-react-native-utils": "13.0.0-alpha.3637360334",
44
43
  "keymirror": "^0.1.1",
45
44
  "query-string": "7.1.3",
46
45
  "react-native-web-linear-gradient": "1.1.2",
@@ -61,12 +60,9 @@
61
60
  "@applicaster/zapp-react-native-tvos-ui-components": "*",
62
61
  "@babel/runtime": "*",
63
62
  "@react-native-community/netinfo": "*",
64
- "@types/node": "*",
65
- "@types/react-native": "0.69.6",
66
63
  "immer": "*",
67
- "react": "17.0.2",
68
- "react-hot-loader": "*",
69
- "react-native": "0.68.6",
64
+ "react": "*",
65
+ "react-native": "*",
70
66
  "react-native-fs": "*",
71
67
  "react-native-gesture-handler": "*",
72
68
  "react-native-linear-gradient": "*",
@@ -55,6 +55,11 @@ const useMuteUnmuteListeners = (playerId, player, asset, setImageUri) =>
55
55
  return;
56
56
  }
57
57
 
58
+ // initial state setup - show correct mute/unmute icon based on current state
59
+ if (player?.playerState?.isReadyToPlay) {
60
+ setImageUri(player.getIsMuted() ? asset.active : asset.inactive);
61
+ }
62
+
58
63
  // active action = muted, inactive = unmuted
59
64
  return player.addListener({
60
65
  id: liveImageButtonsListenerKey,
@@ -73,10 +78,17 @@ const useMuteUnmuteListeners = (playerId, player, asset, setImageUri) =>
73
78
  setImageUri(player?.getIsMuted() ? asset.active : asset.inactive);
74
79
  },
75
80
  onPlayerClose: () => {
76
- setImageUri(null);
81
+ if (isCurrentPlayer(playerId)) {
82
+ setImageUri(null);
83
+ }
77
84
  },
78
85
  onVideoEnd: () => {
79
- setImageUri(null);
86
+ if (isCurrentPlayer(playerId)) {
87
+ setImageUri(null);
88
+ }
89
+ },
90
+ onPlayerResume: () => {
91
+ setImageUri(player?.getIsMuted() ? asset.active : asset.inactive);
80
92
  },
81
93
  },
82
94
  });
@@ -93,7 +105,7 @@ const styles = StyleSheet.create({
93
105
  },
94
106
  });
95
107
 
96
- export function AssetComponent(props: CellActionAssetComponentProps) {
108
+ function AssetImage(props: CellActionAssetComponentProps) {
97
109
  const { cellUUID: playerId, asset } = props;
98
110
  const [imageUri, setImageUri] = useState<Asset>(null);
99
111
 
@@ -108,3 +120,5 @@ export function AssetComponent(props: CellActionAssetComponentProps) {
108
120
 
109
121
  return <View />;
110
122
  }
123
+
124
+ export const AssetComponent = React.memo(AssetImage);
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { Text, TextStyle, View } from "react-native";
2
+ import { Text, TextStyle, View, StyleSheet } from "react-native";
3
3
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
4
4
 
5
5
  type AudioDescriptionProps = {
@@ -13,7 +13,9 @@ type AudioDescriptionProps = {
13
13
  value: GetValue;
14
14
  };
15
15
 
16
- const containerStyles = { paddingTop: 32, paddingBottom: 12 };
16
+ const styles = StyleSheet.create({
17
+ container: {},
18
+ });
17
19
 
18
20
  const titleStyles = (value: GetValue, prefix: string): TextStyle => ({
19
21
  textAlign: "left",
@@ -83,11 +85,11 @@ export const AudioDescription = (props: AudioDescriptionProps) => {
83
85
  const prefix = layoutState.docked ? "docked_player_" : "";
84
86
 
85
87
  return (
86
- <View style={containerStyles}>
88
+ <View style={styles.container}>
87
89
  <Text style={titleStyles(value, prefix)} numberOfLines={1}>
88
90
  {title}
89
91
  </Text>
90
- <Text style={summaryStyles(value, prefix)} numberOfLines={2}>
92
+ <Text style={summaryStyles(value, prefix)} numberOfLines={1}>
91
93
  {summary}
92
94
  </Text>
93
95
  </View>
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { ViewStyle, View } from "react-native";
2
+ import { ViewStyle, View, Dimensions as ScreenDimensions } 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,13 +17,7 @@ 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 {
21
- formatSpeed,
22
- hasDatePassed,
23
- loadItemsFromStorage,
24
- NAMESPACE,
25
- } from "./utils";
26
- import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
20
+ import { formatSpeed } from "./utils";
27
21
  import { addQueryParam, getLiveUrl } from "../Utils";
28
22
  import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
29
23
  import {
@@ -31,8 +25,23 @@ import {
31
25
  ComponentAnimationType,
32
26
  AnimatedScrollModal,
33
27
  } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
34
- import { PADDING_HORIZONTAL } from "./Layout/defaultLayoutsConts";
28
+ import { PADDING_HORIZONTAL } from "./Layout/const";
35
29
  import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
30
+ import {
31
+ speed_0_8,
32
+ speed_1,
33
+ speed_1_2,
34
+ speed_1_5,
35
+ speed_2,
36
+ } from "@applicaster/quick-brick-mobile-transport-controls";
37
+ import {
38
+ SleepTimerController,
39
+ SleepTimerState,
40
+ SLEEP_TIMER_OFF,
41
+ } from "./SleepTimerManager";
42
+ import { SpeedController, PlaybackSpeed, DEFAULT_SPEED } from "./SpeedManager";
43
+ import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
44
+ import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
36
45
 
37
46
  const { log_debug } = createLogger({
38
47
  subsystem: "UIComponent",
@@ -50,6 +59,7 @@ type Configuration = {
50
59
  [key: string]: any;
51
60
  magic_background?: boolean;
52
61
  audio_player_background_image_query?: string;
62
+ audio_player_background_image_default_color?: string;
53
63
  tablet_landscape_sidebar_width?: string;
54
64
  tablet_landscape_player_container_background_color?: string;
55
65
  };
@@ -71,8 +81,7 @@ type Props = {
71
81
  isTabletPortrait?: boolean;
72
82
  };
73
83
 
74
- export const defaultAspectRatioWidth = (height: number): number =>
75
- (height / 9) * 16;
84
+ const { width: SCREEN_WIDTH } = ScreenDimensions.get("screen");
76
85
 
77
86
  const Styles = {
78
87
  mainContainer: (style: ViewStyle) => {
@@ -98,7 +107,11 @@ const Styles = {
98
107
  height: 96,
99
108
  marginHorizontal: 24,
100
109
  },
101
- playerDetails: { marginTop: 60 },
110
+ playerDetails: {
111
+ marginTop: 60,
112
+ alignSelf: "center",
113
+ width: SCREEN_WIDTH - PADDING_HORIZONTAL * 2,
114
+ },
102
115
 
103
116
  linearGradient: {
104
117
  flex: 1,
@@ -116,9 +129,12 @@ const sleeps = [
116
129
  { label: "2 hours", value: 120 },
117
130
  ];
118
131
 
132
+ const edges: Edge[] = ["right", "left", "top"];
133
+
119
134
  export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
120
135
  const player = usePlayer();
121
136
  const playerState = usePlayerState("audio-player-wrapper");
137
+ const theme = useTheme();
122
138
 
123
139
  const {
124
140
  docked,
@@ -132,11 +148,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
132
148
  } = props;
133
149
 
134
150
  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 },
151
+ { asset: configuration["speed_0_8"] || speed_0_8, value: 0.8 },
152
+ { asset: configuration["speed_1"] || speed_1, value: 1.0 },
153
+ { asset: configuration["speed_1_2"] || speed_1_2, value: 1.2 },
154
+ { asset: configuration["speed_1_5"] || speed_1_5, value: 1.5 },
155
+ { asset: configuration["speed_2"] || speed_2, value: 2.0 },
140
156
  ];
141
157
 
142
158
  const isTablet = useIsTablet();
@@ -154,17 +170,29 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
154
170
  configuration?.audio_live_player_update_interval
155
171
  );
156
172
 
157
- const [playbackSpeed, setPlaybackSpeed] = useState({
158
- asset: configuration["speed_1"],
159
- value: 1.0,
160
- });
173
+ const [sleepTimerManager, setSleepTimerManager] =
174
+ useState<SleepTimerController | null>(null);
161
175
 
162
- const [sleepTimer, setSleepTimer] = useState({
163
- label: "playback_sleep_off_label",
164
- value: 0,
165
- });
176
+ const [speedManager, setSpeedManager] = useState<SpeedController | null>(
177
+ null
178
+ );
179
+
180
+ useEffect(() => {
181
+ if (player) {
182
+ setSleepTimerManager(new SleepTimerController(player));
183
+ setSpeedManager(new SpeedController(player));
184
+ } else {
185
+ setSleepTimerManager(null);
186
+ setSpeedManager(null);
187
+ }
188
+ }, [player]);
189
+
190
+ const [playbackSpeed, setPlaybackSpeed] =
191
+ useState<PlaybackSpeed>(DEFAULT_SPEED);
166
192
 
167
- const [sleepTimerDate, setSleepTimerDate] = useState(0);
193
+ const [sleepTimerState, setSleepTimerState] = useState<SleepTimerState>(
194
+ new SleepTimerState(SLEEP_TIMER_OFF, 0)
195
+ );
168
196
 
169
197
  const [defaultDimensions, setDefaultDimensions] =
170
198
  React.useState<Dimensions>(undefined);
@@ -189,82 +217,27 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
189
217
  if (!defaultDimensions) setDefaultDimensions({ width, height });
190
218
  };
191
219
 
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
220
  useEffect(() => {
245
- const loadSpeed = async () => {
246
- const speed = await loadItemsFromStorage("speed");
247
-
248
- if (playerState.isReadyToPlay && speed) {
249
- setPlaybackSpeed(speed);
250
- player?.setPlaybackRate(speed.value);
251
- }
252
- };
221
+ const sleepSubscription =
222
+ sleepTimerManager?.sleepTimerState$.subscribe(setSleepTimerState);
253
223
 
254
- const loadSleep = async () => {
255
- const sleep = await loadItemsFromStorage("sleep");
224
+ const speedSubscription =
225
+ speedManager?.playbackSpeed$.subscribe(setPlaybackSpeed);
256
226
 
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
- }
227
+ return () => {
228
+ sleepSubscription?.unsubscribe();
229
+ speedSubscription?.unsubscribe();
263
230
  };
231
+ }, [sleepTimerManager, speedManager]);
232
+
233
+ useEffect(() => {
234
+ if (!playerState.isReadyToPlay) {
235
+ return;
236
+ }
264
237
 
265
- loadSpeed().then();
266
- loadSleep().then();
267
- }, [playerState.isReadyToPlay]);
238
+ void speedManager?.loadSpeed();
239
+ void sleepTimerManager?.loadSleep();
240
+ }, [playerState.isReadyToPlay, speedManager, sleepTimerManager]);
268
241
 
269
242
  const getValue = (key: string, stylesObj: {}) =>
270
243
  R.propOr(null, key, stylesObj);
@@ -292,7 +265,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
292
265
 
293
266
  const onPlaybackButtonPress = React.useCallback(() => {
294
267
  if (playerState.isBuffering) return;
295
- playerState.isPaused ? player?.play() : player.pause();
268
+ playerState.isPaused ? player?.play() : player?.pause();
296
269
  }, [playerState, player]);
297
270
 
298
271
  const seekDuration = toNumberWithDefault(10, value("seek_duration"));
@@ -307,26 +280,16 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
307
280
 
308
281
  const onSpeedSelect = React.useCallback(
309
282
  async (item) => {
310
- setPlaybackSpeed(item);
311
- player.setPlaybackRate(item.value);
312
- await localStorage.setItem("speed", item, NAMESPACE);
283
+ await speedManager?.setPlaybackSpeed(item);
313
284
  },
314
- [player, setPlaybackSpeed]
285
+ [speedManager]
315
286
  );
316
287
 
317
288
  const onSleepSelect = React.useCallback(
318
289
  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
- );
290
+ await sleepTimerManager?.setSleepTimer(item);
328
291
  },
329
- [player, setSleepTimer]
292
+ [sleepTimerManager]
330
293
  );
331
294
 
332
295
  const onSpeedButtonPress = React.useCallback(() => {
@@ -341,15 +304,24 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
341
304
  supportedOrientations: ["portrait"],
342
305
  },
343
306
  });
344
- }, [playbackSpeed, setPlaybackSpeed]);
307
+ }, [
308
+ playbackSpeed,
309
+ setPlaybackSpeed,
310
+ navigator,
311
+ onSpeedSelect,
312
+ configuration["playback_speed_title"],
313
+ ]);
345
314
 
346
315
  const onSleepButtonPress = React.useCallback(() => {
347
316
  navigator.openModalBottomSheet({
348
317
  modalBottomSheetContentProps: {
349
318
  items: [
350
- { label: configuration["playback_sleep_off_label"], value: 0 },
319
+ {
320
+ label: configuration["playback_sleep_off_label"],
321
+ value: 0,
322
+ },
351
323
  ].concat(sleeps),
352
- current_selection: sleepTimer,
324
+ current_selection: sleepTimerState.timer,
353
325
  onPress: onSleepSelect,
354
326
  title: configuration["playback_sleep_title"],
355
327
  },
@@ -357,7 +329,21 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
357
329
  supportedOrientations: ["portrait"],
358
330
  },
359
331
  });
360
- }, [sleepTimer, setSleepTimer, configuration]);
332
+ }, [
333
+ navigator,
334
+ sleepTimerState.timer,
335
+ configuration["playback_sleep_title"],
336
+ configuration["playback_sleep_off_label"],
337
+ onSleepSelect,
338
+ ]);
339
+
340
+ // This method does not actually cancel the sleep timer
341
+ // (and should not to avoid race condition),
342
+ // it just performs the cleanup when timer expires
343
+ // TODO: rename it in code above
344
+ const cancelSleepTimer = React.useCallback(() => {
345
+ sleepTimerManager?.onTimerExpired();
346
+ }, [sleepTimerManager]);
361
347
 
362
348
  const onCloseVideoModal = React.useCallback(() => {
363
349
  playerManager.close();
@@ -378,13 +364,9 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
378
364
  player?.seekTo(0);
379
365
  }, [player]);
380
366
 
381
- const edges = React.useMemo<Edge[]>(() => ["right", "left", "top"], [docked]);
382
-
383
- const ContainerView = SafeAreaView;
384
-
385
367
  const MainContent = React.useMemo(() => {
386
368
  return (
387
- <ContainerView style={Styles.mainContainer(style)} edges={edges}>
369
+ <SafeAreaView style={Styles.mainContainer(style)} edges={edges}>
388
370
  <AnimationComponent
389
371
  animationType={ComponentAnimationType.moveUpComponent}
390
372
  additionalData={{ saveArea: true }}
@@ -406,9 +388,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
406
388
  onSleepButtonPress={onSleepButtonPress}
407
389
  playbackSpeedName={formatSpeed(playbackSpeed.value)}
408
390
  playbackSleepName={
409
- sleepTimer.value === 0 ? "sleep_timer" : "sleep_timer_active"
391
+ sleepTimerState.timer.value === 0
392
+ ? "sleep_timer"
393
+ : "sleep_timer_active"
410
394
  }
411
- sleepTimerDate={sleepTimerDate}
395
+ sleepTimerDate={sleepTimerState.timerDate}
412
396
  cancelSleepTimer={cancelSleepTimer}
413
397
  onSkipLive={onSkipLive}
414
398
  onCloseVideoModal={onCloseVideoModal}
@@ -437,7 +421,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
437
421
  </View>
438
422
  </AnimatedScrollModal>
439
423
  </AnimationComponent>
440
- </ContainerView>
424
+ </SafeAreaView>
441
425
  );
442
426
  }, [
443
427
  docked,
@@ -458,26 +442,31 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
458
442
  playNextData,
459
443
  onSkipStart,
460
444
  onSkipLive,
461
- sleepTimer.value,
462
- sleepTimerDate,
445
+ sleepTimerState.timer.value,
446
+ sleepTimerState.timerDate,
463
447
  ]);
464
448
 
465
449
  if (configuration.magic_background) {
466
- const baseUrl = entry?.extensions?.audio_player_background_image ?? "";
450
+ const baseUrl = imageSrcFromMediaItem(entry, null);
467
451
 
468
452
  const queryParamName =
469
453
  configuration.audio_player_background_image_query || "width";
470
454
 
455
+ const defaultBackgroundColor =
456
+ configuration.audio_player_background_image_default_color ||
457
+ theme?.app_background_color;
458
+
471
459
  const queryParamValue = 15;
472
460
 
473
- const url = addQueryParam(baseUrl, queryParamName, queryParamValue);
461
+ const url =
462
+ baseUrl && addQueryParam(baseUrl, queryParamName, queryParamValue);
474
463
 
475
464
  return (
476
465
  <MagicBG
477
466
  image={url}
478
467
  fallbackUrl={baseUrl}
479
468
  colors={entry?.extensions?.backgroundColors}
480
- defaultColor="#000000"
469
+ defaultColor={defaultBackgroundColor}
481
470
  >
482
471
  {MainContent}
483
472
  </MagicBG>
@@ -0,0 +1,26 @@
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 { TABLET_VERTICAL_SEPARATOR_HEIGHT } from "../const";
7
+
8
+ import { withDebug } from "../debug";
9
+
10
+ const styles = StyleSheet.create({
11
+ flexingHeight: {
12
+ flex: 1,
13
+
14
+ ...withDebug({ backgroundColor: "red" }),
15
+ },
16
+ fixedHeight: {
17
+ height: TABLET_VERTICAL_SEPARATOR_HEIGHT,
18
+ ...withDebug({ backgroundColor: "red" }),
19
+ },
20
+ });
21
+
22
+ export const VerticalGutter = () => {
23
+ const isTablet = useIsTablet();
24
+
25
+ return <View style={isTablet ? styles.fixedHeight : styles.flexingHeight} />;
26
+ };