@applicaster/zapp-react-native-ui-components 12.0.0-alpha.4433422595 → 12.0.0-alpha.4462655310

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 (78) hide show
  1. package/Components/BaseFocusable/index.tsx +2 -2
  2. package/Components/Cell/Cell.tsx +5 -9
  3. package/Components/Cell/CellWithFocusable.tsx +17 -5
  4. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  5. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  6. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  7. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  8. package/Components/Layout/TV/__tests__/index.test.tsx +19 -75
  9. package/Components/Layout/TV/index.tsx +26 -116
  10. package/Components/Layout/TV/index.web.tsx +33 -0
  11. package/Components/Layout/index.tsx +15 -11
  12. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  13. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -112
  14. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  15. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  16. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  17. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +14 -1
  18. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  19. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  20. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  21. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  22. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +4 -1
  23. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  24. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  25. package/Components/MasterCell/utils/index.ts +33 -35
  26. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  27. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  28. package/Components/Navigator/StackNavigator.tsx +48 -0
  29. package/Components/Navigator/index.ts +1 -0
  30. package/Components/PlayerContainer/PlayerContainer.tsx +23 -3
  31. package/Components/PlayerImageBackground/index.tsx +34 -8
  32. package/Components/River/ComponentsMap/ComponentsMap.tsx +92 -22
  33. package/Components/River/RefreshControl.tsx +55 -1
  34. package/Components/River/TV/River.tsx +0 -23
  35. package/Components/River/__tests__/componentsMap.test.js +6 -0
  36. package/Components/RouteManager/index.tsx +2 -2
  37. package/Components/Screen/TV/hooks/index.ts +3 -0
  38. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  39. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  40. package/Components/Screen/TV/index.web.tsx +119 -28
  41. package/Components/Screen/__tests__/Screen.test.tsx +3 -0
  42. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +4 -0
  43. package/Components/Screen/index.tsx +64 -25
  44. package/Components/ScreenResolver/index.tsx +3 -1
  45. package/Components/Transitioner/Scene.tsx +24 -19
  46. package/Components/Transitioner/Transitioner.tsx +21 -28
  47. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +6 -2
  48. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  49. package/Components/VideoLive/LiveImageManager.ts +18 -1
  50. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +20 -5
  51. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +0 -5
  52. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
  53. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +352 -0
  54. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +158 -0
  55. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +461 -0
  56. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +154 -0
  57. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  58. package/Components/VideoModal/ModalAnimation/utils.ts +299 -0
  59. package/Components/VideoModal/PlayerDetails.tsx +3 -3
  60. package/Components/VideoModal/PlayerWrapper.tsx +77 -47
  61. package/Components/VideoModal/VideoModal.tsx +23 -40
  62. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -1
  63. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +151 -119
  64. package/Components/VideoModal/utils.ts +8 -5
  65. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  66. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  67. package/Components/Viewport/utils.js +1 -1
  68. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  69. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  70. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  71. package/Contexts/ScreenContext/index.tsx +94 -25
  72. package/Contexts/ScreenDataContext/index.tsx +20 -0
  73. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  74. package/events/index.ts +1 -0
  75. package/package.json +5 -5
  76. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  77. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  78. /package/Contexts/{index.js → index.ts} +0 -0
@@ -7,6 +7,10 @@ import { ThemeContext } from "@applicaster/zapp-react-native-utils/theme";
7
7
  import * as navigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
8
8
 
9
9
  import { PlayerLiveImage } from "../PlayerLiveImageComponent";
10
+ import * as useNavigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
11
+
12
+ import { Provider } from "react-redux";
13
+ import configureStore from "redux-mock-store";
10
14
 
11
15
  const seekTo = jest.fn();
12
16
 
@@ -19,14 +23,25 @@ jest.mock(
19
23
  })
20
24
  );
21
25
 
26
+ jest
27
+ .spyOn(useNavigationHooks, "useNavigation")
28
+ .mockImplementation(() => ({ videoModalState: { visible: false } }));
29
+
22
30
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation");
23
31
 
32
+ const mockStore = configureStore();
33
+
34
+ const store = mockStore({ appData: {} });
35
+
24
36
  const Wrapper = ({ children }: { children: React.ReactChildren }) => (
25
- <ThemeContext.Provider
26
- value={{ themes: { myTheme: {} }, selectedThemeId: "myTheme" }}
27
- >
28
- {children}
29
- </ThemeContext.Provider>
37
+ /* @ts-ignore */
38
+ <Provider store={store}>
39
+ <ThemeContext.Provider
40
+ value={{ themes: { myTheme: {} }, selectedThemeId: "myTheme" }}
41
+ >
42
+ {children}
43
+ </ThemeContext.Provider>
44
+ </Provider>
30
45
  );
31
46
 
32
47
  describe("PlayerLiveImageComponent", () => {
@@ -30,11 +30,6 @@ exports[`PlayerLiveImageComponent should render correctly with default props 1`]
30
30
  playerId="player1"
31
31
  resizeMode="cover"
32
32
  screenConfig={Object {}}
33
- source={
34
- Object {
35
- "uri": "",
36
- }
37
- }
38
33
  style={Object {}}
39
34
  />
40
35
  </View>
@@ -0,0 +1,78 @@
1
+ import React from "react";
2
+ import { Animated, ViewStyle } from "react-native";
3
+
4
+ import {
5
+ useModalAnimationContext,
6
+ PlayerAnimationStateEnum,
7
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
8
+ import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks";
9
+
10
+ type Props = {
11
+ style: ViewStyle[];
12
+ children: React.ReactNode;
13
+ };
14
+
15
+ export const AnimatedPlayerModalWrapper = (props: Props) => {
16
+ const {
17
+ playerAnimationState,
18
+ animatedValues: { lastScrollY, dragScrollY, translateYOffset },
19
+ modalSnapPoints,
20
+ setStartComponentsAnimation,
21
+ lastScrollYValue,
22
+ } = useModalAnimationContext();
23
+
24
+ const { height: screenHeight } = useDimensions("screen", {
25
+ deviceInfo: true,
26
+ });
27
+
28
+ const interpolateConfig: Animated.InterpolationConfigType = React.useMemo(
29
+ () => ({
30
+ inputRange: modalSnapPoints,
31
+ outputRange: modalSnapPoints,
32
+ extrapolate: "clamp",
33
+ }),
34
+ [modalSnapPoints]
35
+ );
36
+
37
+ let translateY;
38
+
39
+ if (playerAnimationState === PlayerAnimationStateEnum.drag_player) {
40
+ translateY = translateYOffset.interpolate(interpolateConfig);
41
+ } else {
42
+ const reverseLastScrollY = Animated.multiply(
43
+ new Animated.Value(-1),
44
+ lastScrollY
45
+ );
46
+
47
+ translateY = Animated.add(
48
+ translateYOffset,
49
+ Animated.add(dragScrollY, reverseLastScrollY)
50
+ ).interpolate(interpolateConfig);
51
+ }
52
+
53
+ React.useEffect(() => {
54
+ const translateYListener = translateY.addListener(({ value }) => {
55
+ const preparedValue = Math.round(
56
+ Math.abs(value) - lastScrollYValue.current
57
+ );
58
+
59
+ (playerAnimationState === PlayerAnimationStateEnum.minimize ||
60
+ playerAnimationState === PlayerAnimationStateEnum.maximaze) &&
61
+ setStartComponentsAnimation(
62
+ preparedValue >= Math.round((screenHeight * 10) / 100)
63
+ );
64
+ });
65
+
66
+ return () => {
67
+ translateYListener && translateY.removeListener(translateYListener);
68
+ };
69
+ }, [playerAnimationState]);
70
+
71
+ return (
72
+ <Animated.View
73
+ style={[props.style, { transform: [{ translateY: translateY }] }]}
74
+ >
75
+ {props.children}
76
+ </Animated.View>
77
+ );
78
+ };
@@ -0,0 +1,352 @@
1
+ import React from "react";
2
+ import { Animated, StyleSheet, View, Platform } from "react-native";
3
+
4
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
5
+ import {
6
+ NativeViewGestureHandler,
7
+ PanGestureHandler,
8
+ State,
9
+ TapGestureHandler,
10
+ GestureHandlerRootView,
11
+ } from "react-native-gesture-handler";
12
+
13
+ import { PlayerContainerContext } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainerContext";
14
+ import {
15
+ useModalAnimationContext,
16
+ PlayerAnimationStateEnum,
17
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
18
+ import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
19
+ import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
20
+
21
+ import {
22
+ setScrollModalAnimatedValue,
23
+ resetScrollAnimatedValues,
24
+ } from "./utils";
25
+
26
+ const getAnimatedConfig = (toValue) => {
27
+ return {
28
+ toValue,
29
+ duration: 200,
30
+ useNativeDriver: true,
31
+ };
32
+ };
33
+
34
+ const generalStyles = StyleSheet.create({
35
+ container: {
36
+ flex: 1,
37
+ },
38
+ });
39
+
40
+ type Props = {
41
+ children: React.ReactNode;
42
+ };
43
+
44
+ export const AnimatedScrollModalComponent = ({ children }: Props) => {
45
+ const {
46
+ isActiveGesture,
47
+ playerAnimationState,
48
+ setPlayerAnimationState,
49
+ resetPlayerAnimationState,
50
+ animatedValues: {
51
+ lastScrollY,
52
+ dragScrollY,
53
+ dragVideoPlayerY,
54
+ translateYOffset,
55
+ },
56
+ lastScrollYValue,
57
+ scrollPosition,
58
+ modalSnapPoints,
59
+ lastSnap,
60
+ setLastSnap,
61
+ } = useModalAnimationContext();
62
+
63
+ const { isLanguageOverlayVisible } = React.useContext(PlayerContainerContext);
64
+
65
+ const { maximiseVideoModal, minimiseVideoModal, videoModalState } =
66
+ useNavigation();
67
+
68
+ const isMaximazedModal = videoModalState.mode === "MAXIMIZED";
69
+ const isMinimizedModal = videoModalState.mode === "MINIMIZED";
70
+ const previousItemId = usePrevious(videoModalState.item?.id);
71
+
72
+ const isNotMinimizeMaximazeAnimation =
73
+ playerAnimationState !== PlayerAnimationStateEnum.minimize &&
74
+ playerAnimationState !== PlayerAnimationStateEnum.maximaze;
75
+
76
+ const isEnablePanGesture =
77
+ !isLanguageOverlayVisible &&
78
+ isNotMinimizeMaximazeAnimation &&
79
+ (isMaximazedModal || isMinimizedModal);
80
+
81
+ const isAudioItem = React.useMemo(
82
+ () =>
83
+ videoModalState.item?.content?.type?.includes?.("audio") ||
84
+ videoModalState.item?.type?.value === "audio",
85
+ [videoModalState.item]
86
+ );
87
+
88
+ // Refs for components
89
+ const scrollRef = React.useRef(null);
90
+ const tapHandlerRef = React.useRef(null);
91
+ const panHandlerRef = React.useRef(null);
92
+
93
+ const onRegisterLastScroll = Animated.event(
94
+ [{ nativeEvent: { contentOffset: { y: lastScrollY } } }],
95
+ { useNativeDriver: true }
96
+ );
97
+
98
+ const onGestureEvent = Animated.event(
99
+ [{ nativeEvent: { translationY: dragScrollY } }],
100
+ { useNativeDriver: true }
101
+ );
102
+
103
+ const onHandlerStateChange = React.useCallback(
104
+ ({ nativeEvent }) => {
105
+ if (
106
+ nativeEvent.oldState === State.ACTIVE &&
107
+ scrollPosition.current === 0
108
+ ) {
109
+ // eslint-disable-next-line prefer-const
110
+ const { velocityY, translationY } = nativeEvent;
111
+ const dragToss = 0.05;
112
+
113
+ const preparedTranslationY =
114
+ Math.abs(translationY) - lastScrollYValue.current;
115
+
116
+ if (videoModalState.mode === "MAXIMIZED") {
117
+ const endOffsetY =
118
+ lastSnap + preparedTranslationY + dragToss * velocityY + 150;
119
+
120
+ let destSnapPoint = modalSnapPoints[0];
121
+
122
+ for (const snapPoint of modalSnapPoints) {
123
+ const distFromSnap = Math.abs(snapPoint - endOffsetY);
124
+
125
+ if (distFromSnap < Math.abs(destSnapPoint - endOffsetY)) {
126
+ destSnapPoint = snapPoint;
127
+ }
128
+ }
129
+
130
+ setLastSnap(destSnapPoint);
131
+
132
+ if (destSnapPoint === modalSnapPoints[0]) {
133
+ translateYOffset.extractOffset();
134
+ translateYOffset.setValue(preparedTranslationY);
135
+ translateYOffset.flattenOffset();
136
+ dragScrollY.setValue(0);
137
+
138
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
139
+ } else {
140
+ setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
141
+ }
142
+ } else {
143
+ if (translationY < 0) {
144
+ // from mini to full
145
+ setLastSnap(modalSnapPoints[0]);
146
+
147
+ translateYOffset.setValue(
148
+ modalSnapPoints[1] - preparedTranslationY
149
+ );
150
+
151
+ dragScrollY.setValue(0);
152
+
153
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
154
+ } else {
155
+ resetPlayerAnimationState();
156
+ }
157
+ }
158
+ } else {
159
+ playerAnimationState === PlayerAnimationStateEnum.drag_scroll &&
160
+ resetPlayerAnimationState();
161
+ }
162
+ },
163
+ [lastSnap, modalSnapPoints, playerAnimationState, videoModalState.mode]
164
+ );
165
+
166
+ const onScroll = React.useCallback(({ nativeEvent }) => {
167
+ scrollPosition.current = nativeEvent.contentOffset.y;
168
+ }, []);
169
+
170
+ const onMomentumScrollEnd = React.useCallback(
171
+ ({ nativeEvent }) => {
172
+ if (nativeEvent.contentOffset.y === 0 && !isActiveGesture) {
173
+ resetScrollAnimatedValues(
174
+ lastScrollY,
175
+ lastScrollYValue,
176
+ dragScrollY,
177
+ dragVideoPlayerY
178
+ );
179
+ }
180
+ },
181
+ [isActiveGesture]
182
+ );
183
+
184
+ React.useEffect(() => {
185
+ return () => {
186
+ scrollPosition.current = 0;
187
+
188
+ resetScrollAnimatedValues(
189
+ lastScrollY,
190
+ lastScrollYValue,
191
+ dragScrollY,
192
+ dragVideoPlayerY
193
+ );
194
+ };
195
+ }, []);
196
+
197
+ React.useEffect(() => {
198
+ if (
199
+ videoModalState.mode === "MINIMIZED" &&
200
+ videoModalState.previousMode === "MAXIMIZED"
201
+ ) {
202
+ resetPlayerAnimationState();
203
+ }
204
+
205
+ if (
206
+ (videoModalState.mode === "MAXIMIZED" &&
207
+ videoModalState.previousMode === "MAXIMIZED") ||
208
+ previousItemId !== videoModalState.item?.id
209
+ ) {
210
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
211
+ setTimeout(() => resetPlayerAnimationState());
212
+ }
213
+ }, [videoModalState, previousItemId]);
214
+
215
+ React.useEffect(() => {
216
+ if (playerAnimationState === PlayerAnimationStateEnum.minimize) {
217
+ if (
218
+ (scrollPosition.current === 0 &&
219
+ (lastScrollY as any)._value !== 0 &&
220
+ (dragScrollY as any)._value === 0 &&
221
+ (dragVideoPlayerY as any)._value === 0) ||
222
+ (scrollPosition.current !== 0 &&
223
+ ((dragScrollY as any)._value !== 0 ||
224
+ (dragVideoPlayerY as any)._value !== 0))
225
+ ) {
226
+ resetScrollAnimatedValues(
227
+ lastScrollY,
228
+ lastScrollYValue,
229
+ dragScrollY,
230
+ dragVideoPlayerY
231
+ );
232
+ }
233
+
234
+ Animated.timing(
235
+ translateYOffset,
236
+ getAnimatedConfig(modalSnapPoints[1])
237
+ ).start(() => {
238
+ setTimeout(() => {
239
+ minimiseVideoModal();
240
+
241
+ setScrollModalAnimatedValue(
242
+ translateYOffset,
243
+ modalSnapPoints[1],
244
+ setLastSnap
245
+ );
246
+
247
+ resetScrollAnimatedValues(
248
+ lastScrollY,
249
+ lastScrollYValue,
250
+ dragScrollY,
251
+ dragVideoPlayerY
252
+ );
253
+ }, 150);
254
+ });
255
+ } else if (playerAnimationState === PlayerAnimationStateEnum.maximaze) {
256
+ Animated.timing(translateYOffset, getAnimatedConfig(0)).start(() => {
257
+ maximiseVideoModal();
258
+ setScrollModalAnimatedValue(translateYOffset, 0, setLastSnap);
259
+
260
+ resetScrollAnimatedValues(
261
+ lastScrollY,
262
+ lastScrollYValue,
263
+ dragScrollY,
264
+ dragVideoPlayerY
265
+ );
266
+
267
+ resetPlayerAnimationState();
268
+ });
269
+ }
270
+
271
+ const lastScrollYListenerId = lastScrollY.addListener(({ value }) => {
272
+ lastScrollYValue.current = value;
273
+ });
274
+
275
+ const dragListenerId = dragScrollY.addListener(({ value }) => {
276
+ const preparedValue =
277
+ isMinimizedModal && value >= 0
278
+ ? 0
279
+ : Math.round(isAudioItem ? Math.abs(value) : value);
280
+
281
+ if (
282
+ preparedValue > 0 &&
283
+ scrollPosition.current === 0 &&
284
+ playerAnimationState !== PlayerAnimationStateEnum.drag_player
285
+ ) {
286
+ playerAnimationState !== PlayerAnimationStateEnum.drag_scroll &&
287
+ setPlayerAnimationState(PlayerAnimationStateEnum.drag_scroll);
288
+ }
289
+ });
290
+
291
+ return () => {
292
+ lastScrollY.removeListener(lastScrollYListenerId);
293
+ dragScrollY.removeListener(dragListenerId);
294
+ };
295
+ }, [playerAnimationState, isAudioItem, modalSnapPoints]);
296
+
297
+ const Wrapper = React.useMemo(
298
+ () => (Platform.OS === "android" ? GestureHandlerRootView : View),
299
+ []
300
+ );
301
+
302
+ return (
303
+ <Wrapper style={generalStyles.container}>
304
+ <TapGestureHandler
305
+ maxDurationMs={100000}
306
+ ref={tapHandlerRef}
307
+ maxDeltaY={lastSnap - modalSnapPoints[0]}
308
+ numberOfTaps={1}
309
+ >
310
+ <View pointerEvents="box-none">
311
+ <PanGestureHandler
312
+ enabled={isEnablePanGesture}
313
+ ref={panHandlerRef}
314
+ simultaneousHandlers={[scrollRef, tapHandlerRef]}
315
+ shouldCancelWhenOutside={isMaximazedModal}
316
+ onGestureEvent={onGestureEvent}
317
+ onHandlerStateChange={onHandlerStateChange}
318
+ activeOffsetY={[-5, 5]}
319
+ >
320
+ <Animated.View>
321
+ <NativeViewGestureHandler
322
+ ref={scrollRef}
323
+ waitFor={tapHandlerRef}
324
+ simultaneousHandlers={panHandlerRef}
325
+ >
326
+ <Animated.ScrollView
327
+ scrollEnabled={
328
+ isMaximazedModal && isNotMinimizeMaximazeAnimation
329
+ }
330
+ bounces={false}
331
+ onScrollBeginDrag={onRegisterLastScroll}
332
+ onScroll={onScroll}
333
+ onMomentumScrollEnd={onMomentumScrollEnd}
334
+ scrollEventThrottle={1}
335
+ showsVerticalScrollIndicator={false}
336
+ >
337
+ {children}
338
+ </Animated.ScrollView>
339
+ </NativeViewGestureHandler>
340
+ </Animated.View>
341
+ </PanGestureHandler>
342
+ </View>
343
+ </TapGestureHandler>
344
+ </Wrapper>
345
+ );
346
+ };
347
+
348
+ export const AnimatedScrollModal = ({ children }: Props) => {
349
+ const Component = !isTV() ? AnimatedScrollModalComponent : React.Fragment;
350
+
351
+ return <Component>{children}</Component>;
352
+ };
@@ -0,0 +1,158 @@
1
+ import React from "react";
2
+ import { Animated, StyleSheet } from "react-native";
3
+
4
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
5
+ import { PanGestureHandler, State } from "react-native-gesture-handler";
6
+
7
+ import { PlayerContainerContext } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainerContext";
8
+ import {
9
+ useModalAnimationContext,
10
+ PlayerAnimationStateEnum,
11
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
12
+ import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
13
+
14
+ const generalStyles = StyleSheet.create({
15
+ container: {
16
+ flex: 1,
17
+ width: "100%",
18
+ position: "absolute",
19
+ zIndex: 201,
20
+ },
21
+ });
22
+
23
+ type Props = {
24
+ children: React.ReactNode;
25
+ };
26
+
27
+ export const AnimatedVideoPlayer = ({ children }: Props) => {
28
+ const {
29
+ playerAnimationState,
30
+ setPlayerAnimationState,
31
+ animatedValues: { translateYOffset, dragVideoPlayerY, lastScrollY },
32
+ lastScrollYValue,
33
+ modalSnapPoints,
34
+ lastSnap,
35
+ setLastSnap,
36
+ resetPlayerAnimationState,
37
+ } = useModalAnimationContext();
38
+
39
+ const { videoModalState } = useNavigation();
40
+ const { isLanguageOverlayVisible } = React.useContext(PlayerContainerContext);
41
+
42
+ const isMaximazedModal = videoModalState.mode === "MAXIMIZED";
43
+ const isMinimizedModal = videoModalState.mode === "MINIMIZED";
44
+
45
+ const isEnablePanGesture =
46
+ !isLanguageOverlayVisible && (isMaximazedModal || isMinimizedModal);
47
+
48
+ const onGestureEvent = Animated.event(
49
+ [{ nativeEvent: { translationY: dragVideoPlayerY } }],
50
+ { useNativeDriver: true }
51
+ );
52
+
53
+ const onHandlerStateChange = React.useCallback(
54
+ ({ nativeEvent }) => {
55
+ if (nativeEvent.oldState === State.ACTIVE) {
56
+ // eslint-disable-next-line prefer-const
57
+ const { velocityY, translationY } = nativeEvent;
58
+ const preparedTranslationY = Math.abs(translationY);
59
+ const dragToss = 0.05;
60
+
61
+ if (videoModalState.mode === "MAXIMIZED") {
62
+ if (translationY > 0) {
63
+ const endOffsetY =
64
+ lastSnap + preparedTranslationY + dragToss * velocityY + 200;
65
+
66
+ let destSnapPoint = modalSnapPoints[0];
67
+
68
+ for (const snapPoint of modalSnapPoints) {
69
+ const distFromSnap = Math.abs(snapPoint - endOffsetY);
70
+
71
+ if (distFromSnap < Math.abs(destSnapPoint - endOffsetY)) {
72
+ destSnapPoint = snapPoint;
73
+ }
74
+ }
75
+
76
+ setLastSnap(destSnapPoint);
77
+
78
+ if (destSnapPoint === modalSnapPoints[0]) {
79
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
80
+ } else {
81
+ lastScrollY.setValue(0);
82
+ lastScrollYValue.current = 0;
83
+ setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
84
+ }
85
+ }
86
+ } else if (videoModalState.mode === "MINIMIZED") {
87
+ if (translationY < 0) {
88
+ // from mini to full
89
+ setLastSnap(modalSnapPoints[0]);
90
+
91
+ translateYOffset.setValue(
92
+ modalSnapPoints[1] - preparedTranslationY
93
+ );
94
+
95
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
96
+ } else {
97
+ resetPlayerAnimationState();
98
+ }
99
+ }
100
+ }
101
+ },
102
+ [lastSnap, modalSnapPoints, videoModalState.mode]
103
+ );
104
+
105
+ React.useEffect(() => {
106
+ const dragVideoPlayerYListenerId = dragVideoPlayerY.addListener(
107
+ ({ value }) => {
108
+ if (
109
+ (isMinimizedModal && value >= 0) ||
110
+ (isMaximazedModal && value <= 0)
111
+ ) {
112
+ if (playerAnimationState === PlayerAnimationStateEnum.drag_player) {
113
+ translateYOffset.setValue(
114
+ modalSnapPoints[isMinimizedModal ? 1 : 0]
115
+ );
116
+ }
117
+ } else {
118
+ const preparedValue = Math.round(Math.abs(value));
119
+
120
+ if (
121
+ preparedValue > 0 &&
122
+ playerAnimationState !== PlayerAnimationStateEnum.drag_scroll
123
+ ) {
124
+ playerAnimationState !== PlayerAnimationStateEnum.drag_player &&
125
+ setPlayerAnimationState(PlayerAnimationStateEnum.drag_player);
126
+
127
+ translateYOffset.setValue(
128
+ isMaximazedModal
129
+ ? preparedValue
130
+ : modalSnapPoints[1] - preparedValue
131
+ );
132
+ }
133
+ }
134
+ }
135
+ );
136
+
137
+ return () => {
138
+ dragVideoPlayerY.removeListener(dragVideoPlayerYListenerId);
139
+ };
140
+ }, [playerAnimationState, isMinimizedModal, isMaximazedModal]);
141
+
142
+ return (
143
+ <PanGestureHandler
144
+ enabled={isEnablePanGesture}
145
+ shouldCancelWhenOutside={isMaximazedModal}
146
+ onGestureEvent={onGestureEvent}
147
+ onHandlerStateChange={onHandlerStateChange}
148
+ >
149
+ <Animated.View style={generalStyles.container}>{children}</Animated.View>
150
+ </PanGestureHandler>
151
+ );
152
+ };
153
+
154
+ export const AnimatedVideoPlayerComponent = ({ children }: Props) => {
155
+ const Component = !isTV() ? AnimatedVideoPlayer : React.Fragment;
156
+
157
+ return <Component>{children}</Component>;
158
+ };