@applicaster/quick-brick-player 14.0.0-rc.8 → 14.0.0-rc.80

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 (34) hide show
  1. package/package.json +6 -11
  2. package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +28 -88
  3. package/src/Player/AudioLayer/Layout/Controls/index.tsx +8 -18
  4. package/src/Player/AudioLayer/Layout/DockedControls/index.tsx +119 -125
  5. package/src/Player/AudioLayer/Layout/MobileLayout.tsx +5 -38
  6. package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +3 -8
  7. package/src/Player/AudioLayer/Layout/PlayerImage/FlexImage.tsx +5 -9
  8. package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +22 -72
  9. package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +1 -58
  10. package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +0 -33
  11. package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +6 -18
  12. package/src/Player/AudioLayer/components/SleepTimerModalHeader/SleepTimerModalHeader.tsx +1 -1
  13. package/src/Player/PlayerModal/GestureAnimatedScrollView.ts +7 -0
  14. package/src/Player/PlayerModal/PlayerModal.tsx +115 -0
  15. package/src/Player/PlayerModal/VideoPlayerModal.tsx +271 -0
  16. package/src/Player/PlayerModal/consts/index.ts +17 -0
  17. package/src/Player/PlayerModal/context/index.ts +6 -0
  18. package/src/Player/PlayerModal/hooks/index.ts +201 -0
  19. package/src/Player/PlayerModal/index.ts +3 -0
  20. package/src/Player/PlayerModal/index.web.ts +5 -0
  21. package/src/Player/PlayerModal/styles.ts +104 -0
  22. package/src/Player/PlayerModal/utils/index.ts +29 -0
  23. package/src/Player/PlayerView/GenericTVPlayerView.tsx +42 -0
  24. package/src/Player/PlayerView/TVPlayerView.tsx +26 -0
  25. package/src/Player/PlayerView/TvOSPlayerView.tsx +51 -0
  26. package/src/Player/PlayerView/types.ts +41 -0
  27. package/src/Player/index.tsx +574 -380
  28. package/src/QuickBrickPlayer.tsx +16 -10
  29. package/src/utils/dimensions.ts +29 -0
  30. package/src/utils/index.ts +12 -0
  31. package/src/utils/logger.ts +6 -0
  32. package/src/utils/playerHelpers.ts +11 -0
  33. package/src/utils/playerStyles.ts +50 -0
  34. /package/src/Player/AudioLayer/Layout/{Debug → DebugComponent}/index.tsx +0 -0
@@ -0,0 +1,115 @@
1
+ import React, { ReactElement } from "react";
2
+
3
+ import { Animated, Pressable } from "react-native";
4
+ import { PanGestureHandler } from "react-native-gesture-handler";
5
+ import { useStyles } from "./styles";
6
+ import { useVideoModalState } from "./hooks";
7
+ import { MODAL_COLLAPSE_RATIO, MODAL_RADIUS } from "./consts";
8
+ import { useModalAnimationContext } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
9
+ import { getWindowHeight } from "./utils";
10
+
11
+ type AnimatedModalProps = {
12
+ content?: ReactElement;
13
+ collapsedContent?: ReactElement;
14
+ };
15
+
16
+ export function AnimatedModal({
17
+ content,
18
+ collapsedContent,
19
+ }: AnimatedModalProps) {
20
+ const translateYRef = useModalAnimationContext().yTranslate;
21
+
22
+ const {
23
+ translateY,
24
+ collapsed,
25
+ collapsedHeight,
26
+ gestureHandlerProps,
27
+ expand,
28
+ } = useVideoModalState(translateYRef);
29
+
30
+ const styles = useStyles({ height: collapsedHeight, type: "audio" });
31
+
32
+ const WINDOW_HEIGHT = getWindowHeight();
33
+ const heightAboveMinimised = WINDOW_HEIGHT - collapsedHeight;
34
+
35
+ const MODAL_COLLAPSE_START = WINDOW_HEIGHT * MODAL_COLLAPSE_RATIO;
36
+
37
+ // Interpolated opacities for smooth cross-fade
38
+ const collapsedOpacity = translateY.interpolate({
39
+ inputRange: [MODAL_COLLAPSE_START, heightAboveMinimised],
40
+ outputRange: [0, 1],
41
+ extrapolate: "clamp",
42
+ });
43
+
44
+ const expandedOpacity = translateY.interpolate({
45
+ inputRange: [0, heightAboveMinimised],
46
+ outputRange: [1, 0],
47
+ extrapolate: "clamp",
48
+ });
49
+
50
+ const borderTopRadiusAnimated = translateY.interpolate({
51
+ inputRange: [0, heightAboveMinimised],
52
+ outputRange: [MODAL_RADIUS, 0],
53
+ extrapolate: "clamp",
54
+ });
55
+
56
+ return (
57
+ <PanGestureHandler {...gestureHandlerProps}>
58
+ <Animated.View
59
+ pointerEvents={"auto"}
60
+ style={[
61
+ styles.modalWrapper,
62
+ {
63
+ borderTopLeftRadius: borderTopRadiusAnimated,
64
+ borderTopRightRadius: borderTopRadiusAnimated,
65
+ transform: [{ translateY }],
66
+ height: WINDOW_HEIGHT,
67
+ },
68
+ ]}
69
+ >
70
+ <Animated.View
71
+ style={[
72
+ styles.modal,
73
+ {
74
+ borderTopLeftRadius: borderTopRadiusAnimated,
75
+ borderTopRightRadius: borderTopRadiusAnimated,
76
+ height: WINDOW_HEIGHT,
77
+ },
78
+ ]}
79
+ >
80
+ {/* Expanded Content (fades in) */}
81
+ <Animated.View
82
+ pointerEvents={!collapsed ? "auto" : "none"}
83
+ style={styles.modalContent}
84
+ >
85
+ <Animated.View
86
+ pointerEvents={!collapsed ? "auto" : "none"}
87
+ style={[
88
+ styles.expandedContentContainer,
89
+ { opacity: expandedOpacity },
90
+ ]}
91
+ >
92
+ {content}
93
+ </Animated.View>
94
+ </Animated.View>
95
+ {/* Collapsed Bar (fades out) */}
96
+ <Animated.View
97
+ pointerEvents={collapsed ? "box-none" : "none"}
98
+ style={[
99
+ styles.collapsedBarContainer,
100
+ { opacity: collapsedOpacity },
101
+ ]}
102
+ >
103
+ <Pressable
104
+ testID="collapsedBarWrapper"
105
+ onPress={expand}
106
+ style={styles.collapsedBarWrapper}
107
+ >
108
+ {collapsedContent}
109
+ </Pressable>
110
+ </Animated.View>
111
+ </Animated.View>
112
+ </Animated.View>
113
+ </PanGestureHandler>
114
+ );
115
+ }
@@ -0,0 +1,271 @@
1
+ import React, { ReactElement, useMemo, useRef } from "react";
2
+
3
+ import { Animated, Pressable, StyleSheet, ViewStyle } from "react-native";
4
+ import { PanGestureHandler } from "react-native-gesture-handler";
5
+ import { useStyles } from "./styles";
6
+ import { useConfiguration } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/utils";
7
+ import { useVideoModalState } from "./hooks";
8
+ import {
9
+ DEFAULT_IMAGE_RATIO_VIDEO,
10
+ MODAL_COLLAPSE_RATIO,
11
+ MODAL_RADIUS,
12
+ VIDEO_TRANSITION_THRESHOLD,
13
+ } from "./consts";
14
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
15
+ import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
16
+ import { useModalAnimationContext } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/useModalAnimationContext";
17
+ import { PROGRESS_BAR_HEIGHT } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/utils";
18
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
19
+ import { GestureAnimatedScrollView } from "./GestureAnimatedScrollView";
20
+ import { PlayerDetailsWrapperHeightContext } from "./context";
21
+ import { getWindowHeight, getWindowWidth } from "./utils";
22
+
23
+ const orientationStyles = StyleSheet.create({
24
+ landscape: { flexDirection: "row" },
25
+ portrait: { flexDirection: "column" },
26
+ });
27
+
28
+ const directionStyles = (isLandscape: boolean): ViewStyle => {
29
+ return isLandscape ? orientationStyles.landscape : orientationStyles.portrait;
30
+ };
31
+
32
+ type AnimatedModalProps = {
33
+ pip?: boolean;
34
+ fullscreen?: boolean;
35
+ modal?: boolean;
36
+ content?: ReactElement;
37
+ collapsedContent?: ReactElement;
38
+ extraContent?: ReactElement;
39
+ aspectRatio?: number;
40
+ isTabletPortrait?: boolean;
41
+ style?: {
42
+ tabletLandscapeWidth?: number;
43
+ width?: number;
44
+ height?: number;
45
+ };
46
+ };
47
+
48
+ export function VideoPlayerModal({
49
+ aspectRatio = DEFAULT_IMAGE_RATIO_VIDEO,
50
+ content,
51
+ extraContent,
52
+ collapsedContent,
53
+ isTabletPortrait,
54
+ style,
55
+ pip = false,
56
+ fullscreen = false,
57
+ modal = true,
58
+ }: AnimatedModalProps) {
59
+ const enabled = !!modal && !fullscreen;
60
+ const isTablet = useIsTablet();
61
+
62
+ const WINDOW_WIDTH = useMemo(() => getWindowWidth(), []); // remember initial width, ignore rotation changes
63
+ const height = useMemo(() => getWindowHeight(), []); // remember initial height, ignore rotation changes
64
+ const MODAL_COLLAPSE_START = height * MODAL_COLLAPSE_RATIO;
65
+
66
+ let width = WINDOW_WIDTH;
67
+
68
+ if (isTablet && !isTabletPortrait) {
69
+ width = style?.tabletLandscapeWidth || width;
70
+ }
71
+
72
+ const { minimised_height: minimisedHeight } = useConfiguration();
73
+ const scrollViewRef = useRef<typeof Animated.ScrollView | null>(null);
74
+
75
+ const translateYRef = useModalAnimationContext().yTranslate;
76
+ // used when modal is false, should not be used in animation
77
+ const dummyRef = useRef(new Animated.Value(0));
78
+
79
+ const {
80
+ translateY,
81
+ collapsed,
82
+ collapsedHeight,
83
+ gestureHandlerProps,
84
+ expand,
85
+ } = useVideoModalState(modal ? translateYRef : dummyRef);
86
+
87
+ const heightAboveMinimised = height - collapsedHeight;
88
+
89
+ const isTabletLandscape = !isTV() && isTablet && !isTabletPortrait;
90
+
91
+ const styles = useStyles({ height: collapsedHeight });
92
+
93
+ const SCALE_FACTOR = 1 / (width / aspectRatio / minimisedHeight); // Scale factor for expanded content
94
+
95
+ const insets = useSafeAreaInsets();
96
+
97
+ const isFullScreenOrPIP = fullscreen || pip;
98
+
99
+ // Interpolated opacities for smooth cross-fade
100
+ const collapsedOpacity = !isFullScreenOrPIP
101
+ ? translateY.interpolate({
102
+ inputRange: [MODAL_COLLAPSE_START, heightAboveMinimised],
103
+ outputRange: [0, 1],
104
+ extrapolate: "clamp",
105
+ })
106
+ : new Animated.Value(0);
107
+
108
+ const expandedOpacity = !isFullScreenOrPIP
109
+ ? translateY.interpolate({
110
+ inputRange: [0, heightAboveMinimised],
111
+ outputRange: [1, 0],
112
+ extrapolate: "clamp",
113
+ })
114
+ : new Animated.Value(1);
115
+
116
+ const xPosition = !isFullScreenOrPIP
117
+ ? translateY.interpolate({
118
+ inputRange: [height * VIDEO_TRANSITION_THRESHOLD, heightAboveMinimised],
119
+ outputRange: [0, -(width - width * SCALE_FACTOR) / 2],
120
+ extrapolate: "clamp",
121
+ })
122
+ : new Animated.Value(0);
123
+
124
+ const vidHeight = width / aspectRatio;
125
+
126
+ const yPosition = !isFullScreenOrPIP
127
+ ? translateY.interpolate({
128
+ inputRange: [
129
+ height * VIDEO_TRANSITION_THRESHOLD,
130
+ height - collapsedHeight,
131
+ ],
132
+ outputRange: [
133
+ 0,
134
+ isTablet && isTabletLandscape
135
+ ? -((-PROGRESS_BAR_HEIGHT + height - vidHeight * SCALE_FACTOR) / 2)
136
+ : -(
137
+ PROGRESS_BAR_HEIGHT +
138
+ insets.top / 2 +
139
+ (WINDOW_WIDTH / aspectRatio -
140
+ (WINDOW_WIDTH / aspectRatio) * SCALE_FACTOR) /
141
+ 2
142
+ ),
143
+ ],
144
+ extrapolate: "clamp",
145
+ })
146
+ : new Animated.Value(0);
147
+
148
+ const scalePosition = !isFullScreenOrPIP
149
+ ? translateY.interpolate({
150
+ inputRange: [
151
+ height * VIDEO_TRANSITION_THRESHOLD,
152
+ height - collapsedHeight,
153
+ ],
154
+ outputRange: [1, SCALE_FACTOR],
155
+ extrapolate: "clamp",
156
+ })
157
+ : new Animated.Value(1);
158
+
159
+ const borderTopRadiusAnimated = !isFullScreenOrPIP
160
+ ? translateY.interpolate({
161
+ inputRange: [0, height - collapsedHeight],
162
+ outputRange: [MODAL_RADIUS, 0],
163
+ extrapolate: "clamp",
164
+ })
165
+ : new Animated.Value(0);
166
+
167
+ const [playerDetailsWrapperHeight, setPlayerDetailsWrapperHeight] =
168
+ React.useState(0);
169
+
170
+ const onScrollViewLayout = React.useCallback(
171
+ ({
172
+ nativeEvent: {
173
+ layout: { height },
174
+ },
175
+ }) => {
176
+ setPlayerDetailsWrapperHeight(height);
177
+ },
178
+ []
179
+ );
180
+
181
+ return (
182
+ <PanGestureHandler
183
+ enabled={enabled}
184
+ waitFor={scrollViewRef}
185
+ {...gestureHandlerProps}
186
+ >
187
+ <Animated.View
188
+ pointerEvents={"auto"}
189
+ style={[
190
+ styles.modalWrapper,
191
+ {
192
+ borderTopLeftRadius: borderTopRadiusAnimated,
193
+ borderTopRightRadius: borderTopRadiusAnimated,
194
+ transform: [{ translateY: isFullScreenOrPIP ? 0 : translateY }],
195
+ height: height,
196
+ },
197
+ ]}
198
+ >
199
+ <Animated.View
200
+ pointerEvents={"auto"}
201
+ style={[
202
+ styles.modal,
203
+ collapsed ? styles.collapsedModalBg : null,
204
+ {
205
+ borderTopLeftRadius: borderTopRadiusAnimated,
206
+ borderTopRightRadius: borderTopRadiusAnimated,
207
+ height: height,
208
+ },
209
+ ]}
210
+ >
211
+ <Animated.View
212
+ pointerEvents={!collapsed ? "auto" : "none"}
213
+ style={[styles.modalContent, directionStyles(isTabletLandscape)]}
214
+ >
215
+ <Animated.View
216
+ style={[
217
+ styles.expandedContentContainer,
218
+ {
219
+ transform: [
220
+ { translateX: xPosition },
221
+ { translateY: yPosition },
222
+ { scale: scalePosition },
223
+ ],
224
+ },
225
+ ]}
226
+ >
227
+ {content}
228
+ </Animated.View>
229
+ {extraContent ? (
230
+ <GestureAnimatedScrollView
231
+ ref={scrollViewRef}
232
+ contentContainerStyle={{
233
+ paddingBottom: insets.bottom,
234
+ }}
235
+ onLayout={onScrollViewLayout}
236
+ style={[
237
+ styles.extraContentScrollView,
238
+ { opacity: expandedOpacity },
239
+ ]}
240
+ >
241
+ <PlayerDetailsWrapperHeightContext.Provider
242
+ value={playerDetailsWrapperHeight}
243
+ >
244
+ {extraContent}
245
+ </PlayerDetailsWrapperHeightContext.Provider>
246
+ </GestureAnimatedScrollView>
247
+ ) : null}
248
+ </Animated.View>
249
+
250
+ {!isFullScreenOrPIP ? (
251
+ <Animated.View
252
+ pointerEvents={collapsed ? "box-none" : "none"}
253
+ style={[
254
+ styles.collapsedBarContainer,
255
+ { opacity: collapsedOpacity },
256
+ ]}
257
+ >
258
+ <Pressable
259
+ testID="collapsedBarWrapper"
260
+ onPress={expand}
261
+ style={styles.collapsedBarWrapper}
262
+ >
263
+ {collapsedContent}
264
+ </Pressable>
265
+ </Animated.View>
266
+ ) : null}
267
+ </Animated.View>
268
+ </Animated.View>
269
+ </PanGestureHandler>
270
+ );
271
+ }
@@ -0,0 +1,17 @@
1
+ export const DOCKED_PROGRESSBAR_HEIGHT = 3; // Height of the docked progress bar plugins/mobile-transport-controls/src/ProgressBar/layouts/DockedLayout.tsx
2
+
3
+ export const VIDEO_TRANSITION_THRESHOLD = 0.5; // Threshold for video transition
4
+
5
+ export const DRAG_TO_COLLAPSE = 0.02; // 2% to collapse
6
+
7
+ export const DRAG_TO_EXPAND = 0.02; // 2% to expand
8
+
9
+ export const DEFAULT_IMAGE_RATIO_VIDEO = 16 / 9; // Default aspect ratio for video
10
+
11
+ export const DEFAULT_IMAGE_RATIO_AUDIO = 1; // Default aspect ratio for audio
12
+
13
+ export const ANIMATION_DURATION = 250; // Default animation duration in milliseconds
14
+
15
+ export const MODAL_RADIUS = 16; // Default border radius for modal
16
+
17
+ export const MODAL_COLLAPSE_RATIO = 0.7; // 70% of the screen height
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+
3
+ export const PlayerDetailsWrapperHeightContext = React.createContext(null);
4
+
5
+ export const usePlayerDetailsWrapperHeight = () =>
6
+ React.useContext(PlayerDetailsWrapperHeightContext);
@@ -0,0 +1,201 @@
1
+ import { useCallback, useEffect, useMemo, useRef } from "react";
2
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
3
+ import { State } from "react-native-gesture-handler";
4
+ import { Animated } from "react-native";
5
+
6
+ import { VideoModalMode } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
7
+ import { useConfiguration } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/utils";
8
+ import { getTabBarHeight } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/getTabBarHeight";
9
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
10
+ import { isMenuVisible } from "@applicaster/zapp-react-native-ui-components/Components/Screen/navigationHandler";
11
+ import {
12
+ ANIMATION_DURATION,
13
+ DRAG_TO_COLLAPSE,
14
+ DRAG_TO_EXPAND,
15
+ } from "../consts";
16
+ import { PROGRESS_BAR_HEIGHT } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation/utils";
17
+ import { usePlugins } from "@applicaster/zapp-react-native-redux";
18
+ import { getWindowHeight } from "../utils";
19
+
20
+ const bottomTabBarHeight = getTabBarHeight();
21
+
22
+ export const useVideoModalState = (
23
+ translateYRef: React.MutableRefObject<Animated.Value>
24
+ ) => {
25
+ const { minimised_height: minimisedHeight } = useConfiguration();
26
+
27
+ const WINDOW_HEIGHT = getWindowHeight();
28
+
29
+ const {
30
+ maximiseVideoModal,
31
+ minimiseVideoModal,
32
+ videoModalState,
33
+ currentRoute,
34
+ screenData,
35
+ } = useNavigation();
36
+
37
+ const plugins = usePlugins();
38
+
39
+ const menuVisible = isMenuVisible(currentRoute, screenData, plugins);
40
+
41
+ const collapsed =
42
+ videoModalState.visible &&
43
+ videoModalState.mode === VideoModalMode.MINIMIZED;
44
+
45
+ const { bottom } = useSafeAreaInsets();
46
+
47
+ const initialBottomOffset = useRef(bottom).current;
48
+
49
+ const collapsedHeight =
50
+ minimisedHeight +
51
+ initialBottomOffset +
52
+ (menuVisible ? bottomTabBarHeight : 0) +
53
+ PROGRESS_BAR_HEIGHT;
54
+
55
+ const heightAboveMinimised = WINDOW_HEIGHT - collapsedHeight;
56
+
57
+ const translateY = translateYRef.current;
58
+
59
+ const handleExpand = useCallback(
60
+ (toValue: number) => {
61
+ return Animated.timing(translateYRef.current, {
62
+ toValue,
63
+ duration: ANIMATION_DURATION,
64
+ useNativeDriver: true,
65
+ });
66
+ },
67
+ [translateYRef]
68
+ );
69
+
70
+ const handleCollapse = useCallback(
71
+ (toValue: number) => {
72
+ return Animated.timing(translateYRef.current, {
73
+ toValue,
74
+ duration: ANIMATION_DURATION,
75
+ useNativeDriver: true,
76
+ });
77
+ },
78
+ [translateYRef]
79
+ );
80
+
81
+ const offset = useRef(heightAboveMinimised);
82
+
83
+ // Gesture handler for modal
84
+ const onGestureEvent = useCallback(
85
+ (event: any) => {
86
+ if (event.nativeEvent.state === State.ACTIVE) {
87
+ const y = event.nativeEvent.translationY;
88
+ let newY = offset.current + y;
89
+ if (newY < 0) newY = 0;
90
+
91
+ if (newY > heightAboveMinimised) {
92
+ newY = heightAboveMinimised;
93
+ }
94
+
95
+ translateY.setValue(newY);
96
+ }
97
+ },
98
+ [collapsedHeight, translateY]
99
+ );
100
+
101
+ const onHandlerStateChange = useCallback(
102
+ (event: any) => {
103
+ if (event.nativeEvent.oldState === State.ACTIVE) {
104
+ const { translationY } = event.nativeEvent;
105
+ let newY = offset.current + translationY;
106
+ if (newY < 0) newY = 0;
107
+
108
+ if (newY > heightAboveMinimised) {
109
+ newY = heightAboveMinimised;
110
+ }
111
+
112
+ const shouldCollapse =
113
+ translationY > heightAboveMinimised * DRAG_TO_COLLAPSE;
114
+
115
+ const shouldExpand =
116
+ translationY < -heightAboveMinimised * DRAG_TO_EXPAND;
117
+
118
+ if (!collapsed && shouldCollapse) {
119
+ // Collapse
120
+ Animated.timing(translateY, {
121
+ toValue: heightAboveMinimised,
122
+ duration: ANIMATION_DURATION,
123
+ useNativeDriver: true,
124
+ }).start(() => {
125
+ minimiseVideoModal();
126
+ offset.current = heightAboveMinimised;
127
+ });
128
+ } else if (collapsed && shouldExpand) {
129
+ // Expand
130
+ Animated.timing(translateY, {
131
+ toValue: 0,
132
+ duration: ANIMATION_DURATION,
133
+ useNativeDriver: true,
134
+ }).start(() => {
135
+ maximiseVideoModal();
136
+ offset.current = 0;
137
+ translateY.setValue(0);
138
+ });
139
+ } else {
140
+ // Snap back to current state
141
+ Animated.spring(translateY, {
142
+ toValue: collapsed ? heightAboveMinimised : 0,
143
+ useNativeDriver: true,
144
+ }).start(() => {
145
+ offset.current = collapsed ? heightAboveMinimised : 0;
146
+ translateY.setValue(offset.current);
147
+ });
148
+ }
149
+ } else if (event.nativeEvent.state === State.BEGAN) {
150
+ // Gesture started, set offset
151
+ translateY.stopAnimation((value: number) => {
152
+ offset.current = value;
153
+ });
154
+ }
155
+ },
156
+ [collapsedHeight, translateY, collapsed, videoModalState]
157
+ );
158
+
159
+ const gestureHandlerProps = useMemo(() => {
160
+ return {
161
+ onGestureEvent,
162
+ onHandlerStateChange,
163
+ };
164
+ }, [onGestureEvent, onHandlerStateChange]);
165
+
166
+ useEffect(() => {
167
+ if (videoModalState.visible) {
168
+ if (videoModalState.mode === VideoModalMode.MAXIMIZED) {
169
+ handleExpand(0).start(() => {
170
+ maximiseVideoModal();
171
+ offset.current = 0;
172
+ translateY.setValue(0);
173
+ });
174
+ } else if (videoModalState.mode === VideoModalMode.MINIMIZED) {
175
+ handleCollapse(heightAboveMinimised).start(() => {
176
+ minimiseVideoModal();
177
+ offset.current = heightAboveMinimised;
178
+ translateY.setValue(heightAboveMinimised);
179
+ });
180
+ }
181
+ }
182
+ }, [videoModalState.visible, videoModalState.mode, collapsedHeight]);
183
+
184
+ return useMemo(
185
+ () => ({
186
+ collapsed,
187
+ collapsedHeight,
188
+ offset,
189
+ gestureHandlerProps,
190
+ translateY,
191
+ expand: maximiseVideoModal,
192
+ }),
193
+ [
194
+ collapsed,
195
+ collapsedHeight,
196
+ gestureHandlerProps,
197
+ translateY,
198
+ maximiseVideoModal,
199
+ ]
200
+ );
201
+ };
@@ -0,0 +1,3 @@
1
+ export { AnimatedModal as default } from "./PlayerModal";
2
+
3
+ export { VideoPlayerModal } from "./VideoPlayerModal";
@@ -0,0 +1,5 @@
1
+ const View = require("react-native").View;
2
+
3
+ export const VideoPlayerModal = View;
4
+
5
+ export default View;