@applicaster/zapp-react-native-ui-components 12.0.0-rc.99 → 12.0.2-alpha.1558929238

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 (65) hide show
  1. package/Components/Cell/Cell.tsx +29 -10
  2. package/Components/Focusable/__tests__/index.android.test.tsx +72 -0
  3. package/Components/Focusable/index.android.tsx +15 -9
  4. package/Components/FocusableList/FocusableChild.ts +2 -2
  5. package/Components/FocusableList/index.tsx +11 -12
  6. package/Components/FocusableScrollView/index.tsx +1 -1
  7. package/Components/FreezeWithCallback/Freeze.tsx +31 -0
  8. package/Components/FreezeWithCallback/LICENSE +21 -0
  9. package/Components/FreezeWithCallback/Suspender.tsx +17 -0
  10. package/Components/FreezeWithCallback/__tests__/index.test.tsx +170 -0
  11. package/Components/FreezeWithCallback/index.tsx +3 -0
  12. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +67 -7
  13. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +53 -9
  14. package/Components/Image/Image.android.tsx +1 -1
  15. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +11 -7
  16. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +1 -7
  17. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +6 -1
  18. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +12 -0
  19. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +6 -5
  20. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +2 -2
  21. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +71 -0
  22. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  23. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  24. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +7 -1
  25. package/Components/MasterCell/dataAdapter.ts +21 -7
  26. package/Components/MasterCell/index.tsx +5 -3
  27. package/Components/MasterCell/utils/index.ts +3 -4
  28. package/Components/PlayerContainer/PlayerContainer.tsx +14 -8
  29. package/Components/Screen/TV/hooks/index.ts +0 -2
  30. package/Components/Screen/TV/index.web.tsx +32 -19
  31. package/Components/Screen/__tests__/Screen.test.tsx +0 -49
  32. package/Components/Screen/index.tsx +2 -20
  33. package/Components/Screen/navigationHandler.ts +1 -3
  34. package/Components/Screen/orientationHandler.ts +18 -7
  35. package/Components/Screen/utils.ts +16 -0
  36. package/Components/ScreenResolver/index.tsx +6 -11
  37. package/Components/Transitioner/Scene.tsx +9 -2
  38. package/Components/Transitioner/Transitioner.tsx +38 -23
  39. package/Components/Transitioner/__tests__/Scene.test.js +7 -0
  40. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  41. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +5 -0
  42. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +3 -9
  43. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +87 -41
  44. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +20 -9
  45. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +41 -22
  46. package/Components/VideoModal/PlayerDetails.tsx +16 -2
  47. package/Components/VideoModal/PlayerWrapper.tsx +84 -66
  48. package/Components/VideoModal/VideoModal.tsx +33 -27
  49. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +7 -0
  50. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -0
  51. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +72 -10
  52. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +8 -3
  53. package/Components/ZappFrameworkComponents/BarView/Buttons/BackButton.tsx +7 -2
  54. package/Components/ZappFrameworkComponents/BarView/Buttons/CloseButton.tsx +7 -2
  55. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +87 -0
  56. package/Components/ZappFrameworkComponents/Buttons/ActionButton.tsx +9 -4
  57. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  58. package/Contexts/CachedDimensionsContext/index.ts +13 -9
  59. package/Contexts/ConfirmDialogState/index.ts +1 -1
  60. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +18 -11
  61. package/Contexts/ScreenContext/index.tsx +6 -1
  62. package/const.ts +6 -0
  63. package/index.js +3 -0
  64. package/package.json +5 -5
  65. package/Components/Screen/TV/hooks/useRestoreFocus.ts +0 -22
@@ -1,6 +1,5 @@
1
1
  /// <reference types="@applicaster/applicaster-types" />
2
2
  import React from "react";
3
- import * as R from "ramda";
4
3
  import { View } from "react-native";
5
4
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
6
5
  import {
@@ -27,7 +26,7 @@ import { getNavigationPluginModule } from "@applicaster/zapp-react-native-app/Ap
27
26
 
28
27
  import { RouteManager } from "../RouteManager";
29
28
  import { useScreenConfiguration } from "../River/useScreenConfiguration";
30
- import { useScreenOrientationHandler } from "./orientationHandler";
29
+ import { isValidColor } from "./utils";
31
30
 
32
31
  const screenStyles = {
33
32
  flex: 1,
@@ -37,21 +36,6 @@ const screenStyles = {
37
36
  // paddingRight: insets.right, // Should be applied directly by UI plugin
38
37
  };
39
38
 
40
- function colorIsNotTransparent(color) {
41
- const layers = R.compose(
42
- R.map(R.trim),
43
- R.split(","),
44
- R.replace(")", ""),
45
- R.replace("rgba(", "")
46
- )(color);
47
-
48
- return Number(layers?.[3]) > 0;
49
- }
50
-
51
- function isValidColor(string) {
52
- return string && string !== "transparent" && colorIsNotTransparent(string);
53
- }
54
-
55
39
  type Props = {
56
40
  pathname: unknown;
57
41
  screenData: unknown;
@@ -127,8 +111,6 @@ export function Screen(_props: Props) {
127
111
  const targetScreenData =
128
112
  currentScreenData || (screenData as any)?.targetScreen || screenData;
129
113
 
130
- useScreenOrientationHandler({ screenData: targetScreenData });
131
-
132
114
  const orientation = useGetScreenOrientation(targetScreenData);
133
115
 
134
116
  // We prevent rendering of the screen until UI is actually rotated to screen desired orientation.
@@ -146,7 +128,7 @@ export function Screen(_props: Props) {
146
128
  <View style={style}>
147
129
  {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
148
130
 
149
- <OfflineFallbackScreen screenData={targetScreenData}>
131
+ <OfflineFallbackScreen>
150
132
  {/* @TODO RouteManager doesn't use props, can they be removed ? */}
151
133
  <RouteManager pathname={pathname} screenData={screenData} />
152
134
  </OfflineFallbackScreen>
@@ -26,9 +26,7 @@ export function isMenuVisible(route, screenData) {
26
26
  if (screenData?.hookPlugin) {
27
27
  const hookPlugin = screenData?.hookPlugin?.module;
28
28
 
29
- return (
30
- hookPlugin?.showNavBar || hookPlugin?.presentFullScreen !== true || false
31
- );
29
+ return hookPlugin?.showNavBar || hookPlugin?.presentFullScreen !== true;
32
30
  }
33
31
 
34
32
  if (
@@ -1,14 +1,16 @@
1
1
  import React, { useLayoutEffect } from "react";
2
+
2
3
  import {
3
4
  allowedOrientationsForScreen,
4
5
  ORIENTATIONS,
5
6
  getOrientation,
6
7
  useGetScreenOrientation,
7
8
  } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
8
-
9
+ import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
9
10
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
10
11
  import { findPluginByType } from "@applicaster/zapp-react-native-utils/pluginUtils";
11
12
  import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
13
+
12
14
  import { ZappHookModalContext } from "../../Contexts";
13
15
  import { HookModalContextT } from "../../Contexts/ZappHookModalContext";
14
16
 
@@ -79,14 +81,23 @@ export function useNewOrientationForScreenData({
79
81
  screenOrientation;
80
82
  }
81
83
 
82
- export function useScreenOrientationHandler({
83
- screenData,
84
- }: OrientationHookArgs) {
84
+ type Props = {
85
+ screenData: {
86
+ screen: {};
87
+ };
88
+ isActive: boolean;
89
+ };
90
+
91
+ export function useScreenOrientationHandler({ screenData, isActive }: Props) {
85
92
  const { isHooksExecutionInProgress } = React.useContext<HookModalContextT>(
86
93
  ZappHookModalContext.ReactContext
87
94
  );
88
95
 
89
- const newOrientation = useNewOrientationForScreenData({ screenData });
96
+ const prevIsActive = usePrevious(isActive);
97
+
98
+ const newOrientation = useNewOrientationForScreenData({
99
+ screenData: screenData.screen,
100
+ });
90
101
 
91
102
  useLayoutEffect(() => {
92
103
  // Null means screen does not have orientation preference, so we can skip rotate
@@ -100,12 +111,12 @@ export function useScreenOrientationHandler({
100
111
  return;
101
112
  }
102
113
 
103
- if (newOrientation !== current) {
114
+ if (!prevIsActive && isActive && newOrientation !== current) {
104
115
  current = newOrientation;
105
116
 
106
117
  setOrientation(newOrientation);
107
118
  }
108
- }, [newOrientation, isHooksExecutionInProgress]);
119
+ }, [newOrientation, isHooksExecutionInProgress, prevIsActive, isActive]);
109
120
  }
110
121
 
111
122
  export function useOrientationHandler({ screenData }: OrientationHookArgs) {
@@ -0,0 +1,16 @@
1
+ import { map, split, replace, compose, trim } from "ramda";
2
+
3
+ function colorIsNotTransparent(color) {
4
+ const layers = compose(
5
+ map(trim),
6
+ split(","),
7
+ replace(")", ""),
8
+ replace("rgba(", "")
9
+ )(color);
10
+
11
+ return Number(layers?.[3]) > 0;
12
+ }
13
+
14
+ export function isValidColor(string) {
15
+ return string && string !== "transparent" && colorIsNotTransparent(string);
16
+ }
@@ -1,6 +1,5 @@
1
1
  import * as React from "react";
2
- import * as R from "ramda";
3
- import { useDispatch } from "react-redux";
2
+ import { path, prop } from "ramda";
4
3
  import {
5
4
  findPluginByType,
6
5
  findPluginByIdentifier,
@@ -15,7 +14,6 @@ import { componentsLogger } from "../../Helpers/logger";
15
14
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
16
15
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
17
16
  import { useScreenAnalytics } from "@applicaster/zapp-react-native-utils/analyticsUtils/helpers/hooks";
18
- import { setAppStateHasFinishedTransitioning } from "@applicaster/zapp-react-native-redux/appState";
19
17
 
20
18
  const logger = componentsLogger.addSubsystem("ScreenResolver");
21
19
 
@@ -54,11 +52,8 @@ export function ScreenResolverComponent(props: Props) {
54
52
 
55
53
  const [, setScreenContext] = ZappPipesScreenContext.useZappPipesContext();
56
54
 
57
- const dispatch = useDispatch();
58
-
59
55
  React.useEffect(() => {
60
56
  setScreenContext(rivers[screenId]);
61
- dispatch(setAppStateHasFinishedTransitioning());
62
57
  }, [screenId]);
63
58
 
64
59
  const ScreenPlugin =
@@ -100,14 +95,14 @@ export function ScreenResolverComponent(props: Props) {
100
95
  }
101
96
 
102
97
  const ScreenComponent =
103
- R.path(["module", "Component"], ScreenPlugin) ||
104
- R.prop("module", ScreenPlugin) ||
105
- R.prop("Component", ScreenPlugin) ||
98
+ path(["module", "Component"], ScreenPlugin) ||
99
+ prop("module", ScreenPlugin) ||
100
+ prop("Component", ScreenPlugin) ||
106
101
  ScreenPlugin;
107
102
 
108
103
  const configuration =
109
- R.prop("configuration", ScreenPlugin) ||
110
- R.prop("__plugin_configuration", ScreenComponent);
104
+ prop("configuration", ScreenPlugin) ||
105
+ prop("__plugin_configuration", ScreenComponent);
111
106
 
112
107
  if (ScreenComponent) {
113
108
  return (
@@ -1,6 +1,9 @@
1
1
  import React from "react";
2
2
  import { Animated, ViewProps, ViewStyle } from "react-native";
3
3
  import { useSafeAreaFrame } from "react-native-safe-area-context";
4
+
5
+ import { useScreenOrientationHandler } from "@applicaster/zapp-react-native-ui-components/Components/Screen/orientationHandler";
6
+
4
7
  import { PathnameContext } from "../../Contexts/PathnameContext";
5
8
  import { ScreenDataContext } from "../../Contexts/ScreenDataContext";
6
9
  import { ScreenContextProvider } from "../../Contexts/ScreenContext";
@@ -42,6 +45,11 @@ export function Scene({
42
45
  }: Props) {
43
46
  const [initialScreenData, setInitialScreenData] = React.useState(screenData);
44
47
 
48
+ useScreenOrientationHandler({
49
+ screenData,
50
+ isActive,
51
+ });
52
+
45
53
  React.useLayoutEffect(() => {
46
54
  if (isActive) {
47
55
  setInitialScreenData(screenData);
@@ -49,13 +57,12 @@ export function Scene({
49
57
  }, [screenData, isActive]);
50
58
 
51
59
  const frame = useSafeAreaFrame();
52
-
53
60
  const isAnimating = animating && overlayStyle;
54
61
 
55
62
  const dimensions = isAnimating
56
63
  ? {
57
- height: "100%",
58
64
  width: "100%",
65
+ height: "100%",
59
66
  }
60
67
  : frame;
61
68
 
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { View } from "react-native";
3
- import * as R from "ramda";
3
+ import { update, compose, tail, append, take, identity, isNil } from "ramda";
4
4
 
5
5
  import { AnimationManager } from "./AnimationManager";
6
6
  import { Scene } from "./Scene";
@@ -46,10 +46,14 @@ function prepareScene(children) {
46
46
  */
47
47
  function scenesForBackTransition(props: SceneProps, { scenes }) {
48
48
  return scenes.length > 1
49
- ? R.identity(scenes)
49
+ ? identity(scenes)
50
50
  : [prepareScene(props.children), scenes[0]];
51
51
  }
52
52
 
53
+ const isModalClosed = (navigator) => {
54
+ return !!isNil(navigator?.modalData);
55
+ };
56
+
53
57
  type Props = {
54
58
  transitionConfig: any;
55
59
  children: React.ReactNode;
@@ -110,7 +114,7 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
110
114
  pushTransitionStart() {
111
115
  this.setState(
112
116
  {
113
- scenes: R.append(prepareScene(this.props.children), this.state.scenes),
117
+ scenes: append(prepareScene(this.props.children), this.state.scenes),
114
118
  ...this.animationManager.stateForAction(this.state, NAV_ACTION_PUSH),
115
119
  },
116
120
  () => {
@@ -166,21 +170,23 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
166
170
 
167
171
  transitionWithoutAnimation(resetState) {
168
172
  const { scenes } = this.state;
169
-
170
- const newState = resetState
171
- ? {
172
- scenes: [prepareScene(this.props.children)],
173
- to: {
174
- index: 0,
175
- },
176
- }
177
- : {
178
- scenes: R.update(
179
- scenes.length - 1,
180
- prepareScene(this.props.children),
181
- scenes
182
- ),
183
- };
173
+ const newScene = prepareScene(this.props.children);
174
+ const isLastSceneHook = scenes[scenes.length - 1].key.startsWith("/hook");
175
+
176
+ let newState;
177
+
178
+ if (resetState) {
179
+ newState = {
180
+ scenes: [newScene],
181
+ to: { index: 0 },
182
+ };
183
+ } else if (isLastSceneHook || scenes.length === 1) {
184
+ newState = { scenes: update(scenes.length - 1, newScene, scenes) };
185
+ } else {
186
+ newState = {
187
+ scenes: compose(tail, append(newScene))(scenes),
188
+ };
189
+ }
184
190
 
185
191
  this.setState(newState);
186
192
  }
@@ -223,7 +229,10 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
223
229
  this.shouldSkipAnimationForRoute(previousRoute) ||
224
230
  this.shouldSkipAnimationForRoute(currentRoute)
225
231
  ) {
226
- return this.transitionWithoutAnimation();
232
+ // we are going back, in this case we need to have only one scene using resetState
233
+ const resetState = isGoingBack ? true : undefined;
234
+
235
+ return this.transitionWithoutAnimation(resetState);
227
236
  }
228
237
 
229
238
  switch (this.props.navigator.previousAction) {
@@ -266,7 +275,7 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
266
275
  backTransitionEnd(stepsBack) {
267
276
  this.setState({
268
277
  animating: false,
269
- scenes: R.take(this.state.scenes.length - stepsBack, this.state.scenes),
278
+ scenes: take(this.state.scenes.length - stepsBack, this.state.scenes),
270
279
  });
271
280
  }
272
281
 
@@ -282,7 +291,7 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
282
291
  <View style={styles.container}>
283
292
  {scenes[to.index] && (
284
293
  <Scene
285
- {...{ style: to.style }}
294
+ {...{ style: to?.style || {} }}
286
295
  pathname={pathname}
287
296
  isActive={pathname === this.props.navigator.currentRoute}
288
297
  contentStyle={contentStyle}
@@ -308,7 +317,10 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
308
317
  <Scene
309
318
  key={scenes[from.index].key}
310
319
  pathname={scenes[from.index].key}
311
- isActive={scenes[from.index].key === this.props.navigator.currentRoute}
320
+ isActive={
321
+ scenes[from.index].key === this.props.navigator.currentRoute &&
322
+ isModalClosed(this.props.navigator)
323
+ }
312
324
  style={from.style}
313
325
  pointerEvents="none"
314
326
  overlayStyle={from.overlayStyle}
@@ -324,7 +336,10 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
324
336
  const toScene = scenes[to.index] && (
325
337
  <Scene
326
338
  pathname={scenes[to.index].key}
327
- isActive={scenes[to.index].key === this.props.navigator.currentRoute}
339
+ isActive={
340
+ scenes[to.index].key === this.props.navigator.currentRoute &&
341
+ isModalClosed(this.props.navigator)
342
+ }
328
343
  key={scenes[to.index].key}
329
344
  style={to.style}
330
345
  overlayStyle={to.overlayStyle}
@@ -16,6 +16,13 @@ jest.mock("react-native-safe-area-context", () => ({
16
16
  })),
17
17
  }));
18
18
 
19
+ jest.mock(
20
+ "@applicaster/zapp-react-native-ui-components/Components/Screen/orientationHandler",
21
+ () => ({
22
+ useScreenOrientationHandler: jest.fn(),
23
+ })
24
+ );
25
+
19
26
  const { Scene } = require("../Scene");
20
27
 
21
28
  const wrapper = shallow(
@@ -12,6 +12,7 @@ exports[`<TransitionerComponent /> renders correctly 1`] = `
12
12
  <Scene
13
13
  isActive={true}
14
14
  screenUniqueId="mocked-uuid"
15
+ style={Object {}}
15
16
  >
16
17
  <View
17
18
  foo="bar"
@@ -31,6 +31,11 @@ exports[`PlayerLiveImageComponent should render correctly with default props 1`]
31
31
  playerId="player1"
32
32
  resizeMode="cover"
33
33
  screenConfig={Object {}}
34
+ source={
35
+ Object {
36
+ "uri": "",
37
+ }
38
+ }
34
39
  style={Object {}}
35
40
  />
36
41
  </View>
@@ -45,15 +45,9 @@ export const AnimatedPlayerModalWrapper = (props: Props) => {
45
45
  }
46
46
 
47
47
  React.useEffect(() => {
48
- const translateYListener = translateY.addListener(() => {
49
- (playerAnimationState === PlayerAnimationStateEnum.minimize ||
50
- playerAnimationState === PlayerAnimationStateEnum.maximaze) &&
51
- setStartComponentsAnimation(true);
52
- });
53
-
54
- return () => {
55
- translateYListener && translateY.removeListener(translateYListener);
56
- };
48
+ (playerAnimationState === PlayerAnimationStateEnum.minimize ||
49
+ playerAnimationState === PlayerAnimationStateEnum.maximaze) &&
50
+ setStartComponentsAnimation(true);
57
51
  }, [playerAnimationState]);
58
52
 
59
53
  return (
@@ -61,29 +61,32 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
61
61
  setStartComponentsAnimation,
62
62
  } = useModalAnimationContext();
63
63
 
64
+ const [enableGesture, setIEnableGesture] = React.useState<boolean>(true);
64
65
  const { isLanguageOverlayVisible } = React.useContext(PlayerContainerContext);
65
66
 
66
67
  const { maximiseVideoModal, minimiseVideoModal, videoModalState } =
67
68
  useNavigation();
68
69
 
69
- const isMaximazedModal = videoModalState.mode === "MAXIMIZED";
70
- const isMinimizedModal = videoModalState.mode === "MINIMIZED";
71
- const previousItemId = usePrevious(videoModalState.item?.id);
70
+ const { mode: videoModalMode, item: videoModalItem } = videoModalState;
71
+ const isMaximazedModal = videoModalMode === "MAXIMIZED";
72
+ const isMinimizedModal = videoModalMode === "MINIMIZED";
73
+ const previousItemId = usePrevious(videoModalItem?.id);
72
74
 
73
75
  const isNotMinimizeMaximazeAnimation =
74
76
  playerAnimationState !== PlayerAnimationStateEnum.minimize &&
75
77
  playerAnimationState !== PlayerAnimationStateEnum.maximaze;
76
78
 
77
79
  const isEnablePanGesture =
80
+ enableGesture &&
78
81
  !isLanguageOverlayVisible &&
79
82
  isNotMinimizeMaximazeAnimation &&
80
83
  (isMaximazedModal || isMinimizedModal);
81
84
 
82
85
  const isAudioItem = React.useMemo(
83
86
  () =>
84
- videoModalState.item?.content?.type?.includes?.("audio") ||
85
- videoModalState.item?.type?.value === "audio",
86
- [videoModalState.item]
87
+ videoModalItem?.content?.type?.includes?.("audio") ||
88
+ videoModalItem?.type?.value === "audio",
89
+ [videoModalItem]
87
90
  );
88
91
 
89
92
  // Refs for components
@@ -114,7 +117,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
114
117
  const preparedTranslationY =
115
118
  Math.abs(translationY) - lastScrollYValue.current;
116
119
 
117
- if (videoModalState.mode === "MAXIMIZED") {
120
+ if (videoModalMode === "MAXIMIZED") {
118
121
  const endOffsetY =
119
122
  lastSnap + preparedTranslationY + dragToss * velocityY + 150;
120
123
 
@@ -161,22 +164,38 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
161
164
  resetPlayerAnimationState();
162
165
  }
163
166
  },
164
- [lastSnap, modalSnapPoints, playerAnimationState, videoModalState.mode]
167
+ [lastSnap, modalSnapPoints, playerAnimationState, videoModalMode]
165
168
  );
166
169
 
167
170
  const onScroll = React.useCallback(({ nativeEvent }) => {
168
171
  scrollPosition.current = nativeEvent.contentOffset.y;
169
172
  }, []);
170
173
 
174
+ // Workaround for onMomentumScrollEnd issue
175
+ // https://github.com/facebook/react-native/issues/32696#issuecomment-1104217223
176
+ const canMomentum = React.useRef(false);
177
+
178
+ const onMomentumScrollBegin = React.useCallback(() => {
179
+ canMomentum.current = true;
180
+ }, []);
181
+
171
182
  const onMomentumScrollEnd = React.useCallback(
172
183
  ({ nativeEvent }) => {
173
- if (nativeEvent.contentOffset.y === 0 && !isActiveGesture) {
174
- resetScrollAnimatedValues(
175
- lastScrollY,
176
- lastScrollYValue,
177
- dragScrollY,
178
- dragVideoPlayerY
179
- );
184
+ if (canMomentum.current && !isActiveGesture) {
185
+ if (nativeEvent.contentOffset.y === 0) {
186
+ resetScrollAnimatedValues(
187
+ lastScrollY,
188
+ lastScrollYValue,
189
+ dragScrollY,
190
+ dragVideoPlayerY
191
+ );
192
+
193
+ setIEnableGesture(true);
194
+ } else {
195
+ setIEnableGesture(false);
196
+ }
197
+
198
+ canMomentum.current = false;
180
199
  }
181
200
  },
182
201
  [isActiveGesture]
@@ -196,23 +215,42 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
196
215
  }, []);
197
216
 
198
217
  React.useEffect(() => {
218
+ const { mode, previousMode, item } = videoModalState;
219
+
199
220
  if (
200
- videoModalState.mode === "MINIMIZED" &&
201
- videoModalState.previousMode === "MAXIMIZED"
221
+ mode === "MAXIMIZED" &&
222
+ !enableGesture &&
223
+ scrollPosition.current === 0
202
224
  ) {
203
- resetPlayerAnimationState();
225
+ setIEnableGesture(true);
204
226
  }
205
227
 
206
- if (
207
- (videoModalState.mode === "MAXIMIZED" &&
208
- videoModalState.previousMode === "MINIMIZED" &&
209
- previousItemId === videoModalState.item?.id) ||
210
- previousItemId !== videoModalState.item?.id
228
+ if (mode === "MINIMIZED" && previousMode === "MAXIMIZED") {
229
+ // set animation to the minimize values if moving from the player to another screen
230
+ if (playerAnimationState === null) {
231
+ setScrollModalAnimatedValue(
232
+ translateYOffset,
233
+ modalSnapPoints[1],
234
+ setLastSnap
235
+ );
236
+
237
+ resetScrollAnimatedValues(
238
+ lastScrollY,
239
+ lastScrollYValue,
240
+ dragScrollY,
241
+ dragVideoPlayerY
242
+ );
243
+ }
244
+ } else if (
245
+ playerAnimationState === null &&
246
+ ((previousItemId === item?.id &&
247
+ mode === "MAXIMIZED" &&
248
+ (previousMode === "MINIMIZED" || previousMode === "MAXIMIZED")) ||
249
+ (previousItemId !== item?.id && mode !== "FULLSCREEN"))
211
250
  ) {
212
251
  setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
213
- setTimeout(() => resetPlayerAnimationState());
214
252
  }
215
- }, [videoModalState, previousItemId]);
253
+ }, [videoModalState]);
216
254
 
217
255
  React.useEffect(() => {
218
256
  if (playerAnimationState === PlayerAnimationStateEnum.minimize) {
@@ -237,22 +275,22 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
237
275
  translateYOffset,
238
276
  getAnimatedConfig(modalSnapPoints[1])
239
277
  ).start(() => {
240
- setTimeout(() => {
241
- minimiseVideoModal();
278
+ minimiseVideoModal();
242
279
 
243
- setScrollModalAnimatedValue(
244
- translateYOffset,
245
- modalSnapPoints[1],
246
- setLastSnap
247
- );
280
+ setScrollModalAnimatedValue(
281
+ translateYOffset,
282
+ modalSnapPoints[1],
283
+ setLastSnap
284
+ );
248
285
 
249
- resetScrollAnimatedValues(
250
- lastScrollY,
251
- lastScrollYValue,
252
- dragScrollY,
253
- dragVideoPlayerY
254
- );
255
- }, 150);
286
+ resetScrollAnimatedValues(
287
+ lastScrollY,
288
+ lastScrollYValue,
289
+ dragScrollY,
290
+ dragVideoPlayerY
291
+ );
292
+
293
+ resetPlayerAnimationState();
256
294
  });
257
295
  } else if (playerAnimationState === PlayerAnimationStateEnum.maximaze) {
258
296
  Animated.timing(translateYOffset, getAnimatedConfig(0)).start(() => {
@@ -269,7 +307,9 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
269
307
  resetPlayerAnimationState();
270
308
  });
271
309
  }
310
+ }, [playerAnimationState]);
272
311
 
312
+ React.useEffect(() => {
273
313
  const lastScrollYListenerId = lastScrollY.addListener(({ value }) => {
274
314
  lastScrollYValue.current = value;
275
315
  });
@@ -295,7 +335,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
295
335
  lastScrollY.removeListener(lastScrollYListenerId);
296
336
  dragScrollY.removeListener(dragListenerId);
297
337
  };
298
- }, [playerAnimationState, isAudioItem, modalSnapPoints, isMinimizedModal]);
338
+ }, [playerAnimationState, isAudioItem, isMinimizedModal]);
299
339
 
300
340
  const Wrapper = React.useMemo(
301
341
  () => (Platform.OS === "android" ? GestureHandlerRootView : View),
@@ -333,6 +373,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
333
373
  bounces={false}
334
374
  onScrollBeginDrag={onRegisterLastScroll}
335
375
  onScroll={onScroll}
376
+ onMomentumScrollBegin={onMomentumScrollBegin}
336
377
  onMomentumScrollEnd={onMomentumScrollEnd}
337
378
  scrollEventThrottle={1}
338
379
  showsVerticalScrollIndicator={false}
@@ -349,7 +390,12 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
349
390
  };
350
391
 
351
392
  export const AnimatedScrollModal = ({ children }: Props) => {
352
- const Component = !isTV() ? AnimatedScrollModalComponent : React.Fragment;
393
+ const {
394
+ videoModalState: { visible },
395
+ } = useNavigation();
396
+
397
+ const Component =
398
+ !isTV() && visible ? AnimatedScrollModalComponent : React.Fragment;
353
399
 
354
400
  return <Component>{children}</Component>;
355
401
  };