@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
@@ -9,52 +9,38 @@ import * as R from "ramda";
9
9
  import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
10
10
 
11
11
  import {
12
+ Button,
12
13
  ContentInfo,
13
- ProgressBar,
14
14
  Controls,
15
- Button,
15
+ ProgressBar,
16
16
  } from "@applicaster/quick-brick-mobile-transport-controls";
17
17
 
18
18
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
19
+
19
20
  import {
20
- useModalAnimationContext,
21
21
  AnimationComponent,
22
22
  ComponentAnimationType,
23
23
  PlayerAnimationStateEnum,
24
+ useModalAnimationContext,
24
25
  } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
26
+ import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
27
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
28
+ import { getTabBarHeight } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/getTabBarHeight";
25
29
 
26
- type Dimensions = {
27
- width: number;
28
- height: number;
29
- };
30
+ const bottomTabBarHeight = getTabBarHeight();
30
31
 
31
- type LayoutProps = {
32
+ type Props = {
32
33
  entry: ZappEntry;
33
- layoutState: {
34
- inline: boolean;
35
- docked: boolean;
36
- isModal: boolean;
37
- };
38
- playerState: {
39
- currentTime: number;
40
- duration: number;
41
- seekableDuration: number;
42
- isLive: boolean;
43
- isPaused: boolean;
44
- isBuffering: boolean;
45
- };
34
+ layoutState: QuickBrickPlayer.LayoutState;
35
+ playerState: QuickBrickPlayer.PlayerState;
46
36
  value: GetValue;
47
- configuration: { [key: string]: any };
48
- onPlaybackButtonPress: () => void;
49
- onRewindButtonPress: () => void;
50
- onForwardButtonPress: () => void;
51
- onPlayerSeek: (targetTime: number) => void;
37
+ dimensions: QuickBrickPlayer.AudioPlayerProps["dimensions"];
52
38
  onCloseVideoModal: () => void;
53
- maximiseVideoModal: () => void;
54
- dimensions: Dimensions;
39
+ onPlayerSeek: (targetTime: number) => void;
55
40
  playNextData: PlayNextData;
56
41
  title: string | number;
57
42
  summary: string | number;
43
+ ImageComponent?: React.ReactNode;
58
44
  };
59
45
 
60
46
  const styles = StyleSheet.create({
@@ -71,6 +57,7 @@ const styles = StyleSheet.create({
71
57
  touchableArea: {
72
58
  flexShrink: 1,
73
59
  flex: 1,
60
+ flexDirection: "row",
74
61
  alignSelf: "stretch",
75
62
  },
76
63
  contentInfo: {
@@ -105,25 +92,25 @@ const styles = StyleSheet.create({
105
92
  },
106
93
  });
107
94
 
108
- const backgroundColorStyle = (value: GetValue): ViewStyle => ({
109
- backgroundColor: value("docked_player_background_color"),
110
- });
111
-
112
95
  const playerDimensionsStyle = (
113
96
  minimisedHeight: number,
114
97
  addPadding: boolean
115
98
  ): ViewStyle => ({
116
- paddingLeft: addPadding ? minimisedHeight || 100 : 0,
99
+ width: addPadding ? toNumberWithDefault(100, minimisedHeight) : 0,
117
100
  });
118
101
 
119
- const animatedContainerStyle = (docked: boolean): ViewStyle => ({
102
+ const animatedContainerStyle = (
103
+ docked: boolean,
104
+ progressBarHeight: number
105
+ ): ViewStyle => ({
106
+ top: progressBarHeight,
120
107
  position: "absolute",
121
108
  width: "100%",
122
109
  zIndex: docked ? 1 : -1,
123
110
  });
124
111
 
125
112
  const backgroundImageStyle = (
126
- dimensions: Dimensions,
113
+ dimensions: QuickBrickPlayer.AudioPlayerProps["dimensions"],
127
114
  minimisedHeight: number,
128
115
  isActiveGesture: boolean
129
116
  ): ViewStyle => ({
@@ -133,24 +120,11 @@ const backgroundImageStyle = (
133
120
  });
134
121
 
135
122
  const controlButtons = (
136
- isPlayNextEnabled: boolean,
137
123
  live: boolean,
138
124
  paused: boolean,
139
125
  buffering: boolean,
140
126
  playerId: string
141
127
  ): Array<ControlsButton> => {
142
- if (isPlayNextEnabled) {
143
- return [
144
- {
145
- size: "medium",
146
- name: "play_next",
147
- icon: "play_next",
148
- preferredFocus: true,
149
- style: styles.buttons,
150
- },
151
- ];
152
- }
153
-
154
128
  if (buffering) {
155
129
  return [
156
130
  {
@@ -202,24 +176,23 @@ const controlButtons = (
202
176
  ];
203
177
  };
204
178
 
205
- export default function DockedLayout(props: LayoutProps) {
179
+ export const DockedControls = (props: Props) => {
206
180
  const {
181
+ entry,
207
182
  layoutState,
183
+ playerState,
208
184
  value,
209
185
  dimensions,
210
- entry,
211
186
  onCloseVideoModal,
212
187
  onPlayerSeek,
213
- playerState,
214
- configuration,
215
188
  playNextData,
216
189
  title,
217
190
  summary,
191
+ ImageComponent,
218
192
  } = props;
219
193
 
220
194
  const isTablet = useIsTablet();
221
195
 
222
- const visible = true;
223
196
  const liveButton = Boolean(value("live_badge_enabled"));
224
197
 
225
198
  const {
@@ -227,13 +200,18 @@ export default function DockedLayout(props: LayoutProps) {
227
200
  setPlayerAnimationState,
228
201
  startComponentsAnimation,
229
202
  minimisedHeight,
203
+ progressBarHeight,
230
204
  } = useModalAnimationContext();
231
205
 
232
206
  const maximazeVideoModal = React.useCallback(
233
- () => setPlayerAnimationState(PlayerAnimationStateEnum.maximaze),
207
+ () => setPlayerAnimationState(PlayerAnimationStateEnum.maximize),
234
208
  []
235
209
  );
236
210
 
211
+ const dockedBackgroundColor = value("docked_player_background_color");
212
+
213
+ const insets = useSafeAreaInsets();
214
+
237
215
  if (!layoutState.docked && !startComponentsAnimation) return null;
238
216
 
239
217
  return (
@@ -241,22 +219,29 @@ export default function DockedLayout(props: LayoutProps) {
241
219
  animationType={ComponentAnimationType.componentAppears}
242
220
  style={[
243
221
  styles.container,
244
- animatedContainerStyle(isActiveGesture || layoutState.docked),
222
+ animatedContainerStyle(
223
+ isActiveGesture || layoutState.docked,
224
+ progressBarHeight
225
+ ),
226
+ {
227
+ height: minimisedHeight + bottomTabBarHeight + insets.bottom,
228
+ },
245
229
  ]}
246
230
  >
247
231
  {!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
- )}
232
+ (layoutState.docked || startComponentsAnimation) ? (
233
+ <ProgressBar
234
+ name={"audio-docked-layout"}
235
+ content={entry}
236
+ value={value}
237
+ visible
238
+ layoutState={layoutState}
239
+ onPlayerSeek={onPlayerSeek}
240
+ playerState={playerState}
241
+ startComponentsAnimation={startComponentsAnimation}
242
+ docked
243
+ />
244
+ ) : null}
260
245
 
261
246
  <View style={styles.content}>
262
247
  <View
@@ -267,19 +252,25 @@ export default function DockedLayout(props: LayoutProps) {
267
252
  )}
268
253
  />
269
254
  <TouchableWithoutFeedback onPress={maximazeVideoModal}>
270
- <View
271
- style={[
272
- styles.touchableArea,
273
- playerDimensionsStyle(
274
- minimisedHeight,
275
- isActiveGesture || layoutState.docked
276
- ),
277
- ]}
278
- >
255
+ <View style={styles.touchableArea}>
256
+ {isActiveGesture || layoutState.docked ? (
257
+ <View
258
+ style={{ width: toNumberWithDefault(100, minimisedHeight) }}
259
+ >
260
+ {ImageComponent || null}
261
+ </View>
262
+ ) : (
263
+ <View
264
+ style={playerDimensionsStyle(
265
+ minimisedHeight,
266
+ isActiveGesture || layoutState.docked
267
+ )}
268
+ />
269
+ )}
279
270
  <View
280
271
  style={[
281
272
  styles.contentInfo,
282
- !configuration.magic_background && backgroundColorStyle(value),
273
+ { backgroundColor: dockedBackgroundColor },
283
274
  ]}
284
275
  >
285
276
  <ContentInfo
@@ -295,14 +286,13 @@ export default function DockedLayout(props: LayoutProps) {
295
286
  isTablet
296
287
  ? styles.controlsContainerForTablet
297
288
  : styles.controlsContainerForMobile,
298
- !configuration.magic_background && backgroundColorStyle(value),
289
+ { backgroundColor: dockedBackgroundColor },
299
290
  ]}
300
291
  >
301
292
  <Controls
302
293
  value={value}
303
- visible={visible}
294
+ visible
304
295
  buttons={R.partial(controlButtons, [
305
- playNextData,
306
296
  playerState.isLive && liveButton,
307
297
  ])}
308
298
  style={styles.playPauseButton}
@@ -318,6 +308,12 @@ export default function DockedLayout(props: LayoutProps) {
318
308
  />
319
309
  </View>
320
310
  </View>
311
+ <View
312
+ style={{
313
+ height: insets.bottom + bottomTabBarHeight,
314
+ backgroundColor: dockedBackgroundColor,
315
+ }}
316
+ />
321
317
  </AnimationComponent>
322
318
  );
323
- }
319
+ };
@@ -0,0 +1,230 @@
1
+ import * as React from "react";
2
+ import { StyleSheet, View, ViewStyle } from "react-native";
3
+ import {
4
+ useSafeAreaFrame,
5
+ useSafeAreaInsets,
6
+ } from "react-native-safe-area-context";
7
+ import { useModalAnimationContext } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
8
+
9
+ import { MOBILE, NAVBAR_HEIGHT } from "./const";
10
+ import { TransportInfo } from "./TransportInfo";
11
+ import { PlayerImage } from "./PlayerImage";
12
+ import { Controls } from "./Controls";
13
+
14
+ import { useMobileLayoutImageSize, useTransportInfoHeight } from "./hooks";
15
+ import { DockedControls } from "./DockedControls";
16
+ import { DEBUG, withDebug } from "./debug";
17
+ import {
18
+ BottomDebugSquare,
19
+ LeftDebugSquare,
20
+ RightDebugSquare,
21
+ } from "./Debug/index";
22
+
23
+ import { Props } from "./types";
24
+
25
+ const styles = StyleSheet.create({
26
+ container: {
27
+ flex: 1,
28
+ },
29
+
30
+ flex: {
31
+ flex: 1,
32
+ },
33
+
34
+ audioPlayerContainer: {
35
+ flex: 1,
36
+ justifyContent: "flex-start",
37
+
38
+ minHeight: MOBILE.MIN_HEIGHT,
39
+
40
+ marginLeft: MOBILE.MARGIN_HORIZONTAL,
41
+ marginRight: MOBILE.MARGIN_HORIZONTAL,
42
+
43
+ marginTop: MOBILE.MARGIN_TOP,
44
+
45
+ ...withDebug({
46
+ borderWidth: 2,
47
+ borderColor: "blue",
48
+ }),
49
+ },
50
+ });
51
+
52
+ const mobileStyles = ({
53
+ screenHeight,
54
+ topInset,
55
+ bottomInset,
56
+ imageSize,
57
+ }): ViewStyle => ({
58
+ height: screenHeight - topInset - bottomInset,
59
+
60
+ opacity: imageSize ? 1 : 0, // hide content until imageSize is calculated
61
+
62
+ ...withDebug({
63
+ borderWidth: 2,
64
+ borderColor: "magenta",
65
+ }),
66
+ });
67
+
68
+ export function MobileLayout({
69
+ entry,
70
+ layoutState,
71
+ playerState,
72
+ value,
73
+ onPlaybackButtonPress,
74
+ onRewindButtonPress,
75
+ onForwardButtonPress,
76
+ onPlayerSeek,
77
+ onSkipStart,
78
+ onSkipLive,
79
+ onSpeedButtonPress,
80
+ onSleepButtonPress,
81
+ onCloseVideoModal,
82
+ playbackSpeedName,
83
+ playbackSleepName,
84
+ configuration,
85
+ dimensions,
86
+ playNextData,
87
+ cancelSleepTimer,
88
+ sleepTimerDate,
89
+ title,
90
+ summary,
91
+ }: Props) {
92
+ const { docked } = layoutState;
93
+
94
+ const { bottom: bottomInset, top: topInset } = useSafeAreaInsets();
95
+ const { height: screenHeight } = useSafeAreaFrame();
96
+ const { progressBarHeight } = useModalAnimationContext();
97
+
98
+ const { imageSize } = useMobileLayoutImageSize();
99
+
100
+ const { height: transportInfoHeight, onLayout: onTransportInfoLayout } =
101
+ useTransportInfoHeight();
102
+
103
+ const controlsContainerHeight = React.useMemo(() => {
104
+ return (
105
+ screenHeight -
106
+ topInset -
107
+ imageSize -
108
+ NAVBAR_HEIGHT -
109
+ MOBILE.MARGIN_BOTTOM -
110
+ MOBILE.MARGIN_TOP -
111
+ bottomInset
112
+ );
113
+ }, [screenHeight, topInset, imageSize, bottomInset]);
114
+
115
+ const verticalOffsetForImage = React.useMemo(() => {
116
+ return transportInfoHeight + MOBILE.MARGIN_TOP - progressBarHeight;
117
+ }, [transportInfoHeight, progressBarHeight]);
118
+
119
+ return (
120
+ <View
121
+ style={[
122
+ styles.container,
123
+ mobileStyles({
124
+ screenHeight,
125
+ topInset,
126
+ bottomInset,
127
+ imageSize,
128
+ }),
129
+ ]}
130
+ >
131
+ {DEBUG ? <LeftDebugSquare /> : null}
132
+ {DEBUG ? <RightDebugSquare /> : null}
133
+ {DEBUG ? <BottomDebugSquare /> : null}
134
+
135
+ {/* TransportInfo */}
136
+ <TransportInfo
137
+ entry={entry}
138
+ layoutState={layoutState}
139
+ value={value}
140
+ configuration={configuration}
141
+ onLayout={onTransportInfoLayout}
142
+ />
143
+
144
+ {/* TransportInfo */}
145
+
146
+ <View
147
+ style={[
148
+ styles.audioPlayerContainer,
149
+ {
150
+ width: imageSize,
151
+ },
152
+ ]}
153
+ >
154
+ {/* PlayerImage */}
155
+ <View
156
+ style={[
157
+ styles.flex,
158
+ {
159
+ maxHeight: imageSize,
160
+
161
+ ...withDebug({
162
+ borderWidth: 2,
163
+ borderColor: "yellow",
164
+ }),
165
+ },
166
+ ]}
167
+ >
168
+ <PlayerImage
169
+ entry={entry}
170
+ layoutState={layoutState}
171
+ playerState={playerState}
172
+ value={value}
173
+ configuration={configuration}
174
+ onPlayerSeek={onPlayerSeek}
175
+ onCloseVideoModal={onCloseVideoModal}
176
+ dimensions={dimensions}
177
+ playNextData={playNextData}
178
+ title={title}
179
+ summary={summary}
180
+ docked={docked}
181
+ imageWidth={imageSize}
182
+ verticalOffset={verticalOffsetForImage}
183
+ />
184
+ </View>
185
+ {/* PlayerImage */}
186
+
187
+ <View
188
+ style={{
189
+ height: controlsContainerHeight,
190
+ }}
191
+ >
192
+ <Controls
193
+ entry={entry}
194
+ layoutState={layoutState}
195
+ playerState={playerState}
196
+ value={value}
197
+ title={title}
198
+ summary={summary}
199
+ sleepTimerDate={sleepTimerDate}
200
+ cancelSleepTimer={cancelSleepTimer}
201
+ playbackSpeedName={playbackSpeedName}
202
+ playbackSleepName={playbackSleepName}
203
+ onPlaybackButtonPress={onPlaybackButtonPress}
204
+ onRewindButtonPress={onRewindButtonPress}
205
+ onForwardButtonPress={onForwardButtonPress}
206
+ onPlayerSeek={onPlayerSeek}
207
+ onSkipStart={onSkipStart}
208
+ onSkipLive={onSkipLive}
209
+ onSpeedButtonPress={onSpeedButtonPress}
210
+ onSleepButtonPress={onSleepButtonPress}
211
+ docked={docked}
212
+ />
213
+ </View>
214
+ </View>
215
+
216
+ <DockedControls
217
+ entry={entry}
218
+ layoutState={layoutState}
219
+ playerState={playerState}
220
+ value={value}
221
+ dimensions={dimensions}
222
+ onCloseVideoModal={onCloseVideoModal}
223
+ onPlayerSeek={onPlayerSeek}
224
+ playNextData={playNextData}
225
+ title={title}
226
+ summary={summary}
227
+ />
228
+ </View>
229
+ );
230
+ }
@@ -0,0 +1,70 @@
1
+ import React, { PropsWithChildren } from "react";
2
+ import { Image, Animated, StyleSheet, ImageStyle } from "react-native";
3
+
4
+ import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
5
+ import {
6
+ useModalAnimationContext,
7
+ PlayerAnimationStateEnum,
8
+ DURATION_TO_MINIMIZE,
9
+ DURATION_TO_MAXIMIZE,
10
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
11
+
12
+ const styles = StyleSheet.create({
13
+ container: {
14
+ // we have to specify flex:0 and dimensions to make animation work
15
+ flex: 0,
16
+ aspectRatio: 1,
17
+ },
18
+ });
19
+
20
+ type Props = PropsWithChildren<{
21
+ entry: ZappEntry;
22
+ style?: ImageStyle | ImageStyle[];
23
+ imageWidth: number;
24
+ }>;
25
+
26
+ export const AnimatedImage = ({ entry, style, imageWidth }: Props) => {
27
+ const heightAnim = React.useRef(new Animated.Value(imageWidth)).current;
28
+
29
+ const source = React.useMemo(
30
+ () => ({ uri: imageSrcFromMediaItem(entry, undefined) }),
31
+ [entry]
32
+ );
33
+
34
+ const { playerAnimationState, minimisedHeight } = useModalAnimationContext();
35
+
36
+ React.useEffect(() => {
37
+ if (playerAnimationState === PlayerAnimationStateEnum.minimize) {
38
+ Animated.timing(heightAnim, {
39
+ toValue: minimisedHeight,
40
+ duration: DURATION_TO_MINIMIZE,
41
+ useNativeDriver: false, // width/height can't use native driver
42
+ }).start();
43
+
44
+ return;
45
+ }
46
+
47
+ if (playerAnimationState === PlayerAnimationStateEnum.maximize) {
48
+ Animated.timing(heightAnim, {
49
+ toValue: imageWidth,
50
+ duration: DURATION_TO_MAXIMIZE,
51
+ useNativeDriver: false, // width/height can't use native driver
52
+ }).start();
53
+ }
54
+ }, [playerAnimationState, imageWidth]);
55
+
56
+ if (!source) return null;
57
+
58
+ return (
59
+ <Animated.View
60
+ style={[
61
+ styles.container,
62
+ {
63
+ width: heightAnim,
64
+ },
65
+ ]}
66
+ >
67
+ <Image resizeMode="cover" style={style} source={source} />
68
+ </Animated.View>
69
+ );
70
+ };
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ import { Image, ImageStyle } from "react-native";
3
+
4
+ import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
5
+
6
+ type Props = {
7
+ entry: ZappEntry;
8
+ style?: ImageStyle;
9
+ };
10
+
11
+ export const FlexImage = ({ entry, style }: Props) => {
12
+ const source = React.useMemo(
13
+ () => ({ uri: imageSrcFromMediaItem(entry, undefined) }),
14
+ [entry]
15
+ );
16
+
17
+ if (!source) return null;
18
+
19
+ return <Image resizeMode="cover" style={style} source={source} />;
20
+ };