@applicaster/zapp-react-native-ui-components 12.0.0-alpha.4802689107 → 12.0.0-alpha.4955589444
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.
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +14 -1
- package/Components/MasterCell/utils/index.ts +16 -5
- package/Components/PlayerContainer/PlayerContainer.tsx +14 -2
- package/Components/PlayerImageBackground/index.tsx +34 -8
- package/Components/River/__tests__/componentsMap.test.js +6 -0
- package/Components/Screen/index.tsx +1 -1
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +20 -5
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +342 -0
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +151 -0
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +438 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +143 -0
- package/Components/VideoModal/ModalAnimation/index.ts +15 -0
- package/Components/VideoModal/ModalAnimation/utils.ts +299 -0
- package/Components/VideoModal/PlayerWrapper.tsx +64 -41
- package/Components/VideoModal/VideoModal.tsx +8 -25
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +6 -1
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +151 -119
- package/Components/VideoModal/utils.ts +3 -3
- package/package.json +5 -5
- package/Components/VideoModal/hooks/usePosition.ts +0 -50
|
@@ -177,10 +177,23 @@ const getPlayerConfig = (player_screen_id, actionIdentifier) => {
|
|
|
177
177
|
const positionAbsolute: ViewStyle = { position: "absolute" };
|
|
178
178
|
|
|
179
179
|
const LiveImageComponent = (props: LiveImageProps) => {
|
|
180
|
-
const {
|
|
180
|
+
const {
|
|
181
|
+
item,
|
|
182
|
+
style: componentStyle,
|
|
183
|
+
cellUUID,
|
|
184
|
+
imageKey,
|
|
185
|
+
actionIdentifier,
|
|
186
|
+
state,
|
|
187
|
+
} = props;
|
|
181
188
|
|
|
182
189
|
const component = useUIComponentContext();
|
|
183
190
|
|
|
191
|
+
// Fix for blinking on state change
|
|
192
|
+
const style = React.useMemo(
|
|
193
|
+
() => ({ ...componentStyle, backgroundColor: "transparent" }),
|
|
194
|
+
[componentStyle]
|
|
195
|
+
);
|
|
196
|
+
|
|
184
197
|
const componentType = React.useMemo(
|
|
185
198
|
() => getPropComponentType(component),
|
|
186
199
|
[component?.id]
|
|
@@ -125,13 +125,24 @@ export const useFilterChildren = <
|
|
|
125
125
|
|
|
126
126
|
const filteredChildren = children.filter((child) => {
|
|
127
127
|
const item = child.props.item;
|
|
128
|
-
const
|
|
128
|
+
const actionIdentifier = child.props.pluginIdentifier;
|
|
129
|
+
const action = actions.actions[actionIdentifier];
|
|
130
|
+
|
|
129
131
|
// context value of specific plugin
|
|
130
|
-
|
|
132
|
+
if (action?.module && action.module.context) {
|
|
133
|
+
const currentValue = action.module.context._currentValue;
|
|
134
|
+
|
|
135
|
+
return currentValue?.isActionAvailable
|
|
136
|
+
? currentValue.isActionAvailable(item)
|
|
137
|
+
: true;
|
|
138
|
+
}
|
|
131
139
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
:
|
|
140
|
+
masterCellLogger.error({
|
|
141
|
+
message: `Action plugin for ${actionIdentifier} could not be found, check the configuration of your action button`,
|
|
142
|
+
data: { item, action: child.props.pluginIdentifier },
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return false;
|
|
135
146
|
});
|
|
136
147
|
|
|
137
148
|
return filteredChildren;
|
|
@@ -54,6 +54,11 @@ import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
|
54
54
|
import { usePlayNextOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayNextOverlay";
|
|
55
55
|
import { PlayNextState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/OverlayObserver/OverlaysObserver";
|
|
56
56
|
|
|
57
|
+
import {
|
|
58
|
+
useModalAnimationContext,
|
|
59
|
+
PlayerAnimationStateEnum,
|
|
60
|
+
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
61
|
+
|
|
57
62
|
type Props = {
|
|
58
63
|
Player: React.ComponentType<any>;
|
|
59
64
|
PlayerLoadingView?: React.ComponentType<any>; // 👀 we are not receiving this prop
|
|
@@ -229,6 +234,9 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
229
234
|
const screenData = useTargetScreenData(item);
|
|
230
235
|
const { setVisible: showNavBar } = useSetNavbarState();
|
|
231
236
|
|
|
237
|
+
const { isActiveGesture, startComponentsAnimation, setPlayerAnimationState } =
|
|
238
|
+
useModalAnimationContext();
|
|
239
|
+
|
|
232
240
|
const playerEvent = (event, ...args) => {
|
|
233
241
|
playerManager.invokeHandler(event, ...args);
|
|
234
242
|
};
|
|
@@ -446,7 +454,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
446
454
|
if (disableMiniPlayer) {
|
|
447
455
|
navigator.closeVideoModal();
|
|
448
456
|
} else {
|
|
449
|
-
|
|
457
|
+
setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
|
|
450
458
|
}
|
|
451
459
|
}
|
|
452
460
|
|
|
@@ -641,7 +649,11 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
641
649
|
autoplay={true}
|
|
642
650
|
controls={false}
|
|
643
651
|
disableCastAction={disableCastAction}
|
|
644
|
-
docked={
|
|
652
|
+
docked={
|
|
653
|
+
navigator.isVideoModalDocked() &&
|
|
654
|
+
!startComponentsAnimation &&
|
|
655
|
+
!isActiveGesture
|
|
656
|
+
}
|
|
645
657
|
entry={item}
|
|
646
658
|
fullscreen={mode === VideoModalMode.FULLSCREEN}
|
|
647
659
|
inline={inline}
|
|
@@ -1,38 +1,64 @@
|
|
|
1
1
|
import React, { PropsWithChildren } from "react";
|
|
2
|
-
import { ImageBackground } from "react-native";
|
|
2
|
+
import { ImageBackground, View } from "react-native";
|
|
3
3
|
|
|
4
4
|
import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
5
|
+
import {
|
|
6
|
+
AnimationComponent,
|
|
7
|
+
ComponentAnimationType,
|
|
8
|
+
useModalAnimationContext,
|
|
9
|
+
PlayerAnimationStateEnum,
|
|
10
|
+
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
5
11
|
|
|
6
12
|
type Props = PropsWithChildren<{
|
|
7
13
|
entry: ZappEntry;
|
|
8
14
|
style?: { [K: string]: any };
|
|
9
15
|
imageStyle?: { [K: string]: any };
|
|
10
16
|
imageKey?: string;
|
|
17
|
+
defaultImageDimensions?: { [K: string]: any };
|
|
11
18
|
}>;
|
|
12
19
|
|
|
20
|
+
const imageSize = { width: "100%", height: "100%" };
|
|
21
|
+
|
|
13
22
|
const PlayerImageBackgroundComponent = ({
|
|
14
23
|
entry,
|
|
15
24
|
children,
|
|
16
25
|
style,
|
|
17
26
|
imageStyle,
|
|
18
27
|
imageKey,
|
|
28
|
+
defaultImageDimensions,
|
|
19
29
|
}: Props) => {
|
|
20
30
|
const source = React.useMemo(
|
|
21
31
|
() => ({ uri: imageSrcFromMediaItem(entry, imageKey) }),
|
|
22
32
|
[imageKey, entry]
|
|
23
33
|
);
|
|
24
34
|
|
|
35
|
+
const { playerAnimationState } = useModalAnimationContext();
|
|
36
|
+
|
|
25
37
|
if (!source) return <>{children}</>;
|
|
26
38
|
|
|
27
39
|
return (
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
<View
|
|
41
|
+
style={
|
|
42
|
+
playerAnimationState === PlayerAnimationStateEnum.maximaze
|
|
43
|
+
? defaultImageDimensions
|
|
44
|
+
: style
|
|
45
|
+
}
|
|
33
46
|
>
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
<AnimationComponent
|
|
48
|
+
style={style}
|
|
49
|
+
animationType={ComponentAnimationType.player}
|
|
50
|
+
additionalData={defaultImageDimensions}
|
|
51
|
+
>
|
|
52
|
+
<ImageBackground
|
|
53
|
+
resizeMode="cover"
|
|
54
|
+
style={imageSize}
|
|
55
|
+
imageStyle={imageStyle}
|
|
56
|
+
source={source}
|
|
57
|
+
>
|
|
58
|
+
{children}
|
|
59
|
+
</ImageBackground>
|
|
60
|
+
</AnimationComponent>
|
|
61
|
+
</View>
|
|
36
62
|
);
|
|
37
63
|
};
|
|
38
64
|
|
|
@@ -6,6 +6,12 @@ import configureStore from "redux-mock-store";
|
|
|
6
6
|
|
|
7
7
|
import * as themeUtils from "@applicaster/zapp-react-native-utils/theme";
|
|
8
8
|
|
|
9
|
+
// silences the errors related to React-native-gesture-handler in tests
|
|
10
|
+
jest.mock(
|
|
11
|
+
"@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation",
|
|
12
|
+
() => () => null
|
|
13
|
+
);
|
|
14
|
+
|
|
9
15
|
jest.mock("react-native-safe-area-context", () => ({
|
|
10
16
|
useSafeAreaInsets: () => ({ top: 50 }),
|
|
11
17
|
}));
|
|
@@ -137,7 +137,7 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
|
|
|
137
137
|
{...{ ...(navBarProps as MobileNavBarPluginProps), id: pathname }}
|
|
138
138
|
/>
|
|
139
139
|
|
|
140
|
-
<OfflineFallbackScreen screenData={
|
|
140
|
+
<OfflineFallbackScreen screenData={targetScreenData}>
|
|
141
141
|
{/* @TODO RouteManager doesn't use props, can they be removed ? */}
|
|
142
142
|
<RouteManager pathname={pathname} screenData={screenData} />
|
|
143
143
|
</OfflineFallbackScreen>
|
|
@@ -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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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", () => {
|
|
@@ -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,342 @@
|
|
|
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 = ({ nativeEvent }) => {
|
|
104
|
+
if (nativeEvent.oldState === State.ACTIVE && scrollPosition.current === 0) {
|
|
105
|
+
// eslint-disable-next-line prefer-const
|
|
106
|
+
const { velocityY, translationY } = nativeEvent;
|
|
107
|
+
const dragToss = 0.05;
|
|
108
|
+
|
|
109
|
+
const preparedTranslationY =
|
|
110
|
+
Math.abs(translationY) - lastScrollYValue.current;
|
|
111
|
+
|
|
112
|
+
if (videoModalState.mode === "MAXIMIZED") {
|
|
113
|
+
const endOffsetY =
|
|
114
|
+
lastSnap + preparedTranslationY + dragToss * velocityY + 150;
|
|
115
|
+
|
|
116
|
+
let destSnapPoint = modalSnapPoints[0];
|
|
117
|
+
|
|
118
|
+
for (const snapPoint of modalSnapPoints) {
|
|
119
|
+
const distFromSnap = Math.abs(snapPoint - endOffsetY);
|
|
120
|
+
|
|
121
|
+
if (distFromSnap < Math.abs(destSnapPoint - endOffsetY)) {
|
|
122
|
+
destSnapPoint = snapPoint;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
setLastSnap(destSnapPoint);
|
|
127
|
+
|
|
128
|
+
if (destSnapPoint === modalSnapPoints[0]) {
|
|
129
|
+
translateYOffset.extractOffset();
|
|
130
|
+
translateYOffset.setValue(preparedTranslationY);
|
|
131
|
+
translateYOffset.flattenOffset();
|
|
132
|
+
dragScrollY.setValue(0);
|
|
133
|
+
|
|
134
|
+
setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
|
|
135
|
+
} else {
|
|
136
|
+
setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
if (translationY < 0) {
|
|
140
|
+
// from mini to full
|
|
141
|
+
setLastSnap(modalSnapPoints[0]);
|
|
142
|
+
translateYOffset.setValue(modalSnapPoints[1] - preparedTranslationY);
|
|
143
|
+
dragScrollY.setValue(0);
|
|
144
|
+
|
|
145
|
+
setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
|
|
146
|
+
} else {
|
|
147
|
+
resetPlayerAnimationState();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
playerAnimationState === PlayerAnimationStateEnum.drag_scroll &&
|
|
152
|
+
resetPlayerAnimationState();
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const onScroll = React.useCallback(({ nativeEvent }) => {
|
|
157
|
+
scrollPosition.current = nativeEvent.contentOffset.y;
|
|
158
|
+
}, []);
|
|
159
|
+
|
|
160
|
+
const onMomentumScrollEnd = React.useCallback(
|
|
161
|
+
({ nativeEvent }) => {
|
|
162
|
+
if (nativeEvent.contentOffset.y === 0 && !isActiveGesture) {
|
|
163
|
+
resetScrollAnimatedValues(
|
|
164
|
+
lastScrollY,
|
|
165
|
+
lastScrollYValue,
|
|
166
|
+
dragScrollY,
|
|
167
|
+
dragVideoPlayerY
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
[isActiveGesture]
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
React.useEffect(() => {
|
|
175
|
+
return () => {
|
|
176
|
+
scrollPosition.current = 0;
|
|
177
|
+
|
|
178
|
+
resetScrollAnimatedValues(
|
|
179
|
+
lastScrollY,
|
|
180
|
+
lastScrollYValue,
|
|
181
|
+
dragScrollY,
|
|
182
|
+
dragVideoPlayerY
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
}, []);
|
|
186
|
+
|
|
187
|
+
React.useEffect(() => {
|
|
188
|
+
if (
|
|
189
|
+
videoModalState.mode === "MINIMIZED" &&
|
|
190
|
+
videoModalState.previousMode === "MAXIMIZED"
|
|
191
|
+
) {
|
|
192
|
+
resetPlayerAnimationState();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (
|
|
196
|
+
(videoModalState.mode === "MAXIMIZED" &&
|
|
197
|
+
videoModalState.previousMode === "MAXIMIZED") ||
|
|
198
|
+
previousItemId !== videoModalState.item?.id
|
|
199
|
+
) {
|
|
200
|
+
setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
|
|
201
|
+
setTimeout(() => resetPlayerAnimationState());
|
|
202
|
+
}
|
|
203
|
+
}, [videoModalState, previousItemId]);
|
|
204
|
+
|
|
205
|
+
React.useEffect(() => {
|
|
206
|
+
if (playerAnimationState === PlayerAnimationStateEnum.minimize) {
|
|
207
|
+
if (
|
|
208
|
+
(scrollPosition.current === 0 &&
|
|
209
|
+
(lastScrollY as any)._value !== 0 &&
|
|
210
|
+
(dragScrollY as any)._value === 0 &&
|
|
211
|
+
(dragVideoPlayerY as any)._value === 0) ||
|
|
212
|
+
(scrollPosition.current !== 0 &&
|
|
213
|
+
((dragScrollY as any)._value !== 0 ||
|
|
214
|
+
(dragVideoPlayerY as any)._value !== 0))
|
|
215
|
+
) {
|
|
216
|
+
resetScrollAnimatedValues(
|
|
217
|
+
lastScrollY,
|
|
218
|
+
lastScrollYValue,
|
|
219
|
+
dragScrollY,
|
|
220
|
+
dragVideoPlayerY
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
Animated.timing(
|
|
225
|
+
translateYOffset,
|
|
226
|
+
getAnimatedConfig(modalSnapPoints[1])
|
|
227
|
+
).start(() => {
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
minimiseVideoModal();
|
|
230
|
+
|
|
231
|
+
setScrollModalAnimatedValue(
|
|
232
|
+
translateYOffset,
|
|
233
|
+
modalSnapPoints[1],
|
|
234
|
+
setLastSnap
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
resetScrollAnimatedValues(
|
|
238
|
+
lastScrollY,
|
|
239
|
+
lastScrollYValue,
|
|
240
|
+
dragScrollY,
|
|
241
|
+
dragVideoPlayerY
|
|
242
|
+
);
|
|
243
|
+
}, 150);
|
|
244
|
+
});
|
|
245
|
+
} else if (playerAnimationState === PlayerAnimationStateEnum.maximaze) {
|
|
246
|
+
Animated.timing(translateYOffset, getAnimatedConfig(0)).start(() => {
|
|
247
|
+
maximiseVideoModal();
|
|
248
|
+
setScrollModalAnimatedValue(translateYOffset, 0, setLastSnap);
|
|
249
|
+
|
|
250
|
+
resetScrollAnimatedValues(
|
|
251
|
+
lastScrollY,
|
|
252
|
+
lastScrollYValue,
|
|
253
|
+
dragScrollY,
|
|
254
|
+
dragVideoPlayerY
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
resetPlayerAnimationState();
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const lastScrollYListenerId = lastScrollY.addListener(({ value }) => {
|
|
262
|
+
lastScrollYValue.current = value;
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const dragListenerId = dragScrollY.addListener(({ value }) => {
|
|
266
|
+
const preparedValue =
|
|
267
|
+
isMinimizedModal && value >= 0
|
|
268
|
+
? 0
|
|
269
|
+
: Math.round(isAudioItem ? Math.abs(value) : value);
|
|
270
|
+
|
|
271
|
+
if (
|
|
272
|
+
preparedValue > 0 &&
|
|
273
|
+
scrollPosition.current === 0 &&
|
|
274
|
+
playerAnimationState !== PlayerAnimationStateEnum.drag_player
|
|
275
|
+
) {
|
|
276
|
+
playerAnimationState !== PlayerAnimationStateEnum.drag_scroll &&
|
|
277
|
+
setPlayerAnimationState(PlayerAnimationStateEnum.drag_scroll);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
return () => {
|
|
282
|
+
lastScrollY.removeListener(lastScrollYListenerId);
|
|
283
|
+
dragScrollY.removeListener(dragListenerId);
|
|
284
|
+
};
|
|
285
|
+
}, [playerAnimationState, isAudioItem, modalSnapPoints]);
|
|
286
|
+
|
|
287
|
+
const Wrapper = React.useMemo(
|
|
288
|
+
() => (Platform.OS === "android" ? GestureHandlerRootView : View),
|
|
289
|
+
[]
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
return (
|
|
293
|
+
<Wrapper style={generalStyles.container}>
|
|
294
|
+
<TapGestureHandler
|
|
295
|
+
maxDurationMs={100000}
|
|
296
|
+
ref={tapHandlerRef}
|
|
297
|
+
maxDeltaY={lastSnap - modalSnapPoints[0]}
|
|
298
|
+
numberOfTaps={1}
|
|
299
|
+
>
|
|
300
|
+
<View pointerEvents="box-none">
|
|
301
|
+
<PanGestureHandler
|
|
302
|
+
enabled={isEnablePanGesture}
|
|
303
|
+
ref={panHandlerRef}
|
|
304
|
+
simultaneousHandlers={[scrollRef, tapHandlerRef]}
|
|
305
|
+
shouldCancelWhenOutside={isMaximazedModal}
|
|
306
|
+
onGestureEvent={onGestureEvent}
|
|
307
|
+
onHandlerStateChange={onHandlerStateChange}
|
|
308
|
+
activeOffsetY={[-5, 5]}
|
|
309
|
+
>
|
|
310
|
+
<Animated.View>
|
|
311
|
+
<NativeViewGestureHandler
|
|
312
|
+
ref={scrollRef}
|
|
313
|
+
waitFor={tapHandlerRef}
|
|
314
|
+
simultaneousHandlers={panHandlerRef}
|
|
315
|
+
>
|
|
316
|
+
<Animated.ScrollView
|
|
317
|
+
scrollEnabled={
|
|
318
|
+
isMaximazedModal && isNotMinimizeMaximazeAnimation
|
|
319
|
+
}
|
|
320
|
+
bounces={false}
|
|
321
|
+
onScrollBeginDrag={onRegisterLastScroll}
|
|
322
|
+
onScroll={onScroll}
|
|
323
|
+
onMomentumScrollEnd={onMomentumScrollEnd}
|
|
324
|
+
scrollEventThrottle={1}
|
|
325
|
+
showsVerticalScrollIndicator={false}
|
|
326
|
+
>
|
|
327
|
+
{children}
|
|
328
|
+
</Animated.ScrollView>
|
|
329
|
+
</NativeViewGestureHandler>
|
|
330
|
+
</Animated.View>
|
|
331
|
+
</PanGestureHandler>
|
|
332
|
+
</View>
|
|
333
|
+
</TapGestureHandler>
|
|
334
|
+
</Wrapper>
|
|
335
|
+
);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
export const AnimatedScrollModal = ({ children }: Props) => {
|
|
339
|
+
const Component = !isTV() ? AnimatedScrollModalComponent : React.Fragment;
|
|
340
|
+
|
|
341
|
+
return <Component>{children}</Component>;
|
|
342
|
+
};
|