@applicaster/zapp-react-native-ui-components 14.0.0-alpha.4517121861 → 14.0.0-alpha.4520122433

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 (126) hide show
  1. package/Components/AnimatedInOut/index.tsx +5 -3
  2. package/Components/AudioPlayer/mobile/Layout.tsx +1 -1
  3. package/Components/AudioPlayer/tv/helpers.tsx +10 -3
  4. package/Components/BaseFocusable/index.tsx +23 -12
  5. package/Components/Cell/Cell.tsx +91 -64
  6. package/Components/Cell/CellWithFocusable.tsx +3 -0
  7. package/Components/Cell/__tests__/CellWIthFocusable.test.js +3 -2
  8. package/Components/Cell/index.js +1 -1
  9. package/Components/ComponentResolver/index.ts +1 -1
  10. package/Components/FeedLoader/FeedLoader.tsx +7 -16
  11. package/Components/FeedLoader/FeedLoaderHOC.tsx +21 -0
  12. package/Components/FeedLoader/index.js +2 -8
  13. package/Components/Focusable/Focusable.tsx +12 -3
  14. package/Components/Focusable/FocusableTvOS.tsx +5 -5
  15. package/Components/Focusable/FocusableiOS.tsx +2 -2
  16. package/Components/Focusable/Touchable.tsx +5 -3
  17. package/Components/Focusable/index.android.tsx +8 -4
  18. package/Components/Focusable/index.tsx +1 -1
  19. package/Components/FocusableGroup/FocusableTvOS.tsx +1 -1
  20. package/Components/FocusableList/FocusableItem.tsx +4 -3
  21. package/Components/FocusableList/FocusableListItemWrapper.tsx +2 -1
  22. package/Components/FocusableList/hooks/useCellState.android.ts +13 -3
  23. package/Components/FocusableList/index.tsx +20 -9
  24. package/Components/FreezeWithCallback/__tests__/index.test.tsx +67 -43
  25. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +42 -59
  26. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +13 -10
  27. package/Components/HandlePlayable/HandlePlayable.tsx +17 -10
  28. package/Components/Layout/TV/LayoutBackground.tsx +1 -1
  29. package/Components/Layout/TV/__tests__/index.test.tsx +0 -1
  30. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +6 -2
  31. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -1
  32. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -39
  33. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +1 -1
  34. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +1 -1
  35. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +65 -17
  36. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +21 -3
  37. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +6 -3
  38. package/Components/MasterCell/DefaultComponents/Text/index.tsx +26 -6
  39. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +10 -10
  40. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +18 -18
  41. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/index.test.tsx +10 -10
  42. package/Components/MasterCell/elementMapper.tsx +1 -2
  43. package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
  44. package/Components/MasterCell/utils/index.ts +11 -5
  45. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
  46. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
  47. package/Components/OfflineHandler/__tests__/index.test.tsx +26 -35
  48. package/Components/PlayerContainer/ErrorDisplay/index.ts +1 -1
  49. package/Components/PlayerContainer/PlayerContainer.tsx +45 -29
  50. package/Components/PlayerContainer/ProgramInfo/index.tsx +1 -1
  51. package/Components/PlayerContainer/index.ts +1 -1
  52. package/Components/River/ComponentsMap/ComponentsMap.tsx +0 -1
  53. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +378 -0
  54. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +2 -2
  55. package/Components/River/RefreshControl.tsx +11 -17
  56. package/Components/River/TV/River.tsx +2 -17
  57. package/Components/River/TV/index.tsx +3 -1
  58. package/Components/River/TV/withPipesV1DataLoader.tsx +43 -0
  59. package/Components/River/TV/withRiverDataLoader.tsx +17 -0
  60. package/Components/River/TV/withTVEventHandler.tsx +1 -1
  61. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  62. package/Components/River/__tests__/river.test.js +12 -26
  63. package/Components/River/index.tsx +1 -1
  64. package/Components/Screen/__tests__/Screen.test.tsx +28 -29
  65. package/Components/Screen/__tests__/navigationHandler.test.ts +133 -22
  66. package/Components/Screen/navigationHandler.ts +20 -2
  67. package/Components/ScreenRevealManager/ScreenRevealManager.ts +76 -0
  68. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +107 -0
  69. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +96 -0
  70. package/Components/ScreenRevealManager/index.ts +1 -0
  71. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +79 -0
  72. package/Components/Tabs/TV/Tabs.android.tsx +1 -3
  73. package/Components/Tabs/Tabs.tsx +2 -3
  74. package/Components/TextInputTv/__tests__/__snapshots__/TextInputTv.test.js.snap +13 -0
  75. package/Components/TextInputTv/index.tsx +11 -0
  76. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
  77. package/Components/Touchable/__tests__/touchable.test.tsx +12 -17
  78. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
  79. package/Components/VideoLive/animationUtils.ts +3 -3
  80. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +3 -9
  81. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +294 -0
  82. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +93 -0
  83. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +73 -29
  84. package/Components/VideoModal/PlayerDetails.tsx +24 -2
  85. package/Components/VideoModal/PlayerWrapper.tsx +26 -142
  86. package/Components/VideoModal/VideoModal.tsx +3 -17
  87. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -5
  88. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -7
  89. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +44 -240
  90. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +9 -1
  91. package/Components/VideoModal/hooks/index.ts +0 -2
  92. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +40 -15
  93. package/Components/VideoModal/hooks/useModalSize.ts +18 -2
  94. package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +2 -2
  95. package/Components/VideoModal/hooks/utils/index.ts +4 -0
  96. package/Components/VideoModal/utils.ts +6 -0
  97. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
  98. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
  99. package/Contexts/CellFocusedStateContext/index.tsx +27 -0
  100. package/Contexts/ConfigutaionContext/__tests__/ConfigurationProvider.test.tsx +3 -3
  101. package/Contexts/ScreenContext/index.tsx +46 -6
  102. package/Decorators/ConfigurationWrapper/__tests__/withConfigurationProvider.test.tsx +3 -3
  103. package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
  104. package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +221 -209
  105. package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +14 -16
  106. package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -20
  107. package/Decorators/RiverFeedLoader/index.tsx +22 -4
  108. package/Decorators/RiverFeedLoader/utils/index.ts +0 -18
  109. package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +3 -6
  110. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -0
  111. package/Decorators/ZappPipesDataConnector/__tests__/NullFeedResolver.test.tsx +78 -0
  112. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +205 -0
  113. package/Decorators/ZappPipesDataConnector/__tests__/StaticFeedResolver.test.tsx +251 -0
  114. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +368 -0
  115. package/Decorators/ZappPipesDataConnector/__tests__/utils.test.ts +39 -0
  116. package/Decorators/ZappPipesDataConnector/index.tsx +26 -293
  117. package/Decorators/ZappPipesDataConnector/resolvers/NullFeedResolver.tsx +25 -0
  118. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +87 -0
  119. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +266 -0
  120. package/Decorators/ZappPipesDataConnector/types.ts +29 -0
  121. package/Decorators/ZappPipesDataConnector/utils/mongoFilter.ts +738 -0
  122. package/Decorators/ZappPipesDataConnector/utils/useFilter.tsx +157 -0
  123. package/events/index.ts +3 -0
  124. package/package.json +5 -10
  125. package/Components/River/__tests__/__snapshots__/river.test.js.snap +0 -27
  126. package/Components/VideoModal/hooks/useBackgroundColor.ts +0 -10
@@ -0,0 +1,79 @@
1
+ import * as React from "react";
2
+ import { Animated } from "react-native";
3
+ import { isFirstComponentScreenPicker } from "@applicaster/zapp-react-native-utils/componentsUtils";
4
+ import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
5
+ import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
6
+
7
+ import { ScreenRevealManager } from "./ScreenRevealManager";
8
+
9
+ const flex = platformSelect({
10
+ tvos: 1,
11
+ android_tv: 1,
12
+ web: undefined,
13
+ samsung_tv: undefined,
14
+ lg_tv: undefined,
15
+ default: undefined,
16
+ });
17
+
18
+ export const TIMEOUT = 500; // 500 ms
19
+
20
+ const HIDDEN = 0; // opacity = 0
21
+
22
+ export const SHOWN = 1; // opacity = 1
23
+
24
+ type Props = {
25
+ componentsToRender: ZappUIComponent[];
26
+ };
27
+
28
+ export const withScreenRevealManager = (Component) => {
29
+ return function WithScreenRevealManager(props: Props) {
30
+ const { componentsToRender } = props;
31
+
32
+ const [isReadyToShow, setIsReadyToShow] = React.useState(false);
33
+
34
+ const handleSetIsReadyToShow = React.useCallback(() => {
35
+ setIsReadyToShow(true);
36
+ }, []);
37
+
38
+ const managerRef = useRefWithInitialValue<ScreenRevealManager>(
39
+ () => new ScreenRevealManager(componentsToRender, handleSetIsReadyToShow)
40
+ );
41
+
42
+ const opacityRef = useRefWithInitialValue<Animated.Value>(
43
+ () => new Animated.Value(HIDDEN)
44
+ );
45
+
46
+ React.useEffect(() => {
47
+ if (isReadyToShow) {
48
+ Animated.timing(opacityRef.current, {
49
+ toValue: SHOWN,
50
+ duration: TIMEOUT,
51
+ useNativeDriver: true,
52
+ }).start();
53
+ }
54
+ }, [isReadyToShow]);
55
+
56
+ if (isFirstComponentScreenPicker(componentsToRender)) {
57
+ // for screen-picker with have additional internal ComponentsMap, no need to add this wrapper
58
+ return <Component {...props} />;
59
+ }
60
+
61
+ return (
62
+ <Animated.View
63
+ style={{ opacity: opacityRef.current, flex }}
64
+ testID="animated-component"
65
+ >
66
+ <Component
67
+ {...props}
68
+ initialNumberToLoad={
69
+ managerRef.current.numberOfComponentsWaitToLoadBeforePresent
70
+ }
71
+ onLoadFinishedFromScreenRevealManager={
72
+ managerRef.current.onLoadFinished
73
+ }
74
+ onLoadFailedFromScreenRevealManager={managerRef.current.onLoadFailed}
75
+ />
76
+ </Animated.View>
77
+ );
78
+ };
79
+ };
@@ -7,7 +7,6 @@ import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
7
7
 
8
8
  import Tab from "./Tab";
9
9
  import { Gutter } from "../Gutter";
10
- import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
11
10
  import { ImageBackground, View } from "react-native";
12
11
  import { getStyles } from "./styles";
13
12
  import {
@@ -63,7 +62,7 @@ const TabsComponent = ({
63
62
  );
64
63
 
65
64
  const renderItem = useCallback(
66
- ({ item, index, focused }) => {
65
+ ({ item, index, focused }: { item: any; index: any; focused?: any }) => {
67
66
  const itemId = generateFocusableId(getId(item));
68
67
 
69
68
  const isSelected = R.equals(index, selectedEntryIndex);
@@ -100,7 +99,6 @@ const TabsComponent = ({
100
99
  >
101
100
  <FocusableList
102
101
  horizontal
103
- onScrollToIndexFailed={noop}
104
102
  onLayout={onLayoutChange}
105
103
  contentContainerStyle={tabsListContentContainer}
106
104
  ref={flatListRef}
@@ -158,12 +158,11 @@ const TabsComponent = ({
158
158
 
159
159
  const renderItem = React.useCallback(
160
160
  ({ item, index, item: { id } }: RenderItemProps) => (
161
- <>
161
+ <React.Fragment key={id}>
162
162
  <Tab
163
163
  ref={(ref) => {
164
164
  tabRefs.current[index] = ref;
165
165
  }}
166
- key={id}
167
166
  {...{
168
167
  title: getTitle(item),
169
168
  id,
@@ -177,7 +176,7 @@ const TabsComponent = ({
177
176
  {display_mode === "fractional" &&
178
177
  index !== tabs?.length - 1 &&
179
178
  renderGutter()}
180
- </>
179
+ </React.Fragment>
181
180
  ),
182
181
  [tabs, display_mode, configuration, onTabPress, getTitle, getSelectedItem]
183
182
  );
@@ -2,6 +2,19 @@
2
2
 
3
3
  exports[`<TextInputTv /> renders 1`] = `
4
4
  <input
5
+ accessibilityProps={
6
+ {
7
+ "accessibilityHint": "Enter text into Search",
8
+ "accessibilityLabel": "Search",
9
+ "accessibilityRole": "searchbox",
10
+ "accessible": true,
11
+ "aria-description": "Enter text into Search",
12
+ "aria-label": "Search",
13
+ "aria-role": "searchbox",
14
+ "role": "searchbox",
15
+ "tabindex": 0,
16
+ }
17
+ }
5
18
  testID="TextInput-tv"
6
19
  />
7
20
  `;
@@ -4,6 +4,7 @@ import { Appearance, Platform, StyleSheet, TextInput } from "react-native";
4
4
  import { isFunction } from "@applicaster/zapp-react-native-utils/functionUtils";
5
5
  import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
6
6
  import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
7
+ import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
7
8
 
8
9
  type Props = Partial<{
9
10
  style: any;
@@ -42,6 +43,8 @@ function TextInputTV(props: Props, ref) {
42
43
  const [colorScheme, setColorScheme] = useState(getInitialColorScheme());
43
44
  const isRTL = useIsRTL();
44
45
 
46
+ const accessibilityManager = useAccessibilityManager({});
47
+
45
48
  const onColorChange = useCallback(
46
49
  ({ colorScheme: color }) => {
47
50
  if (color !== colorScheme) {
@@ -153,6 +156,13 @@ function TextInputTV(props: Props, ref) {
153
156
  ])
154
157
  )(props);
155
158
 
159
+ const getAccessibilityProps = () => {
160
+ return {
161
+ accessibilityProps:
162
+ accessibilityManager.getInputAccessibilityProps("Search"),
163
+ };
164
+ };
165
+
156
166
  const inputProps = {
157
167
  ...getProps(),
158
168
  ...getStyle(),
@@ -161,6 +171,7 @@ function TextInputTV(props: Props, ref) {
161
171
  ...getSecureTextEntry(),
162
172
  ...getOnEndEditing(),
163
173
  ...getOnPress(),
174
+ ...getAccessibilityProps(),
164
175
  };
165
176
 
166
177
  if (
@@ -3,6 +3,23 @@
3
3
  exports[`<Touchable /> when not running in automated tests environment renders correctly 1`] = `
4
4
  <View
5
5
  accessibilityLabel="some-test-id"
6
+ accessibilityState={
7
+ {
8
+ "busy": undefined,
9
+ "checked": undefined,
10
+ "disabled": undefined,
11
+ "expanded": undefined,
12
+ "selected": undefined,
13
+ }
14
+ }
15
+ accessibilityValue={
16
+ {
17
+ "max": undefined,
18
+ "min": undefined,
19
+ "now": undefined,
20
+ "text": undefined,
21
+ }
22
+ }
6
23
  accessible={true}
7
24
  collapsable={false}
8
25
  focusable={true}
@@ -29,6 +46,23 @@ exports[`<Touchable /> when not running in automated tests environment renders c
29
46
  exports[`<Touchable /> when running in automated tests environment has accessible flag set to false 1`] = `
30
47
  <View
31
48
  accessibilityLabel="some-test-id"
49
+ accessibilityState={
50
+ {
51
+ "busy": undefined,
52
+ "checked": undefined,
53
+ "disabled": undefined,
54
+ "expanded": undefined,
55
+ "selected": undefined,
56
+ }
57
+ }
58
+ accessibilityValue={
59
+ {
60
+ "max": undefined,
61
+ "min": undefined,
62
+ "now": undefined,
63
+ "text": undefined,
64
+ }
65
+ }
32
66
  accessible={false}
33
67
  collapsable={false}
34
68
  focusable={true}
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { View, TouchableOpacity } from "react-native";
3
- import { create, act } from "react-test-renderer";
3
+ import { render } from "@testing-library/react-native";
4
4
 
5
5
  import { Touchable } from "..";
6
6
 
@@ -23,38 +23,31 @@ describe("<Touchable />", () => {
23
23
  });
24
24
 
25
25
  it("has accessible flag set to false", () => {
26
- let wrapper;
26
+ const { toJSON, UNSAFE_getByType } = render(<Touchable {...props} />);
27
27
 
28
- act(() => {
29
- wrapper = create(<Touchable {...props} />);
30
- });
31
-
32
- const touchableWrapper = wrapper.root.findByType(TouchableOpacity);
33
- expect(wrapper.toJSON()).toMatchSnapshot();
28
+ const touchableWrapper = UNSAFE_getByType(TouchableOpacity);
29
+ expect(toJSON()).toMatchSnapshot();
34
30
  expect(touchableWrapper.props).toHaveProperty("accessible", false);
35
31
  });
36
32
  });
37
33
 
38
34
  describe("when not running in automated tests environment", () => {
39
- let wrapper;
40
-
41
- act(() => {
42
- wrapper = create(<Touchable {...props} />);
43
- });
44
-
45
- const touchableWrapper = wrapper.root.findByType(TouchableOpacity);
46
-
47
35
  beforeEach(props.onPress.mockClear);
48
36
 
49
37
  it("renders correctly", () => {
50
- expect(wrapper.toJSON()).toMatchSnapshot();
38
+ const { toJSON } = render(<Touchable {...props} />);
39
+ expect(toJSON()).toMatchSnapshot();
51
40
  });
52
41
 
53
42
  it("has accessible flag set to true", () => {
43
+ const { UNSAFE_getByType } = render(<Touchable {...props} />);
44
+ const touchableWrapper = UNSAFE_getByType(TouchableOpacity);
54
45
  expect(touchableWrapper.props).toHaveProperty("accessible", true);
55
46
  });
56
47
 
57
48
  it("assigns testID and accessibilityLabel props correctly", () => {
49
+ const { UNSAFE_getByType } = render(<Touchable {...props} />);
50
+ const touchableWrapper = UNSAFE_getByType(TouchableOpacity);
58
51
  expect(touchableWrapper.props).toHaveProperty("testID", props.testID);
59
52
 
60
53
  expect(touchableWrapper.props).toHaveProperty(
@@ -64,6 +57,8 @@ describe("<Touchable />", () => {
64
57
  });
65
58
 
66
59
  it("calls the onPress event when it is pressed", () => {
60
+ const { UNSAFE_getByType } = render(<Touchable {...props} />);
61
+ const touchableWrapper = UNSAFE_getByType(TouchableOpacity);
67
62
  touchableWrapper.props.onPress();
68
63
  expect(props.onPress).toHaveBeenCalledTimes(1);
69
64
  });
@@ -6,15 +6,21 @@ exports[`<Scene /> renders correctly 1`] = `
6
6
  collapsable={false}
7
7
  pointerEvents="auto"
8
8
  style={
9
- {
10
- "flex": 1,
11
- "fontScale": 2,
12
- "height": 1334,
13
- "paddingBottom": 49,
14
- "scale": 2,
15
- "statusBarHeight": null,
16
- "width": 750,
17
- }
9
+ [
10
+ {
11
+ "flex": 1,
12
+ },
13
+ {
14
+ "paddingBottom": 49,
15
+ },
16
+ {
17
+ "fontScale": 2,
18
+ "height": 1334,
19
+ "scale": 2,
20
+ "statusBarHeight": null,
21
+ "width": 750,
22
+ },
23
+ ]
18
24
  }
19
25
  />
20
26
  </View>
@@ -1,8 +1,8 @@
1
1
  import { Animated, Easing, EasingFunction, StyleProp } from "react-native";
2
2
 
3
3
  type AnimatedInterpolatedStyle =
4
- | Animated.AnimatedInterpolation
5
- | [{ [Key: string]: Animated.AnimatedInterpolation }];
4
+ | Animated.AnimatedInterpolation<number>
5
+ | [{ [Key: string]: Animated.AnimatedInterpolation<number> }];
6
6
 
7
7
  type AnimationConfig = {
8
8
  duration: number;
@@ -31,7 +31,7 @@ const interpolate = (
31
31
  animatedValue: Animated.Value,
32
32
  from: number = 0,
33
33
  to: number = 1
34
- ): Animated.AnimatedInterpolation =>
34
+ ): Animated.AnimatedInterpolation<number> =>
35
35
  animatedValue.interpolate({
36
36
  inputRange: [0, 1],
37
37
  outputRange: [from, to],
@@ -1,9 +1,8 @@
1
1
  import React from "react";
2
- import { Animated, Platform, StyleSheet, View } from "react-native";
2
+ import { Animated, StyleSheet, View } from "react-native";
3
3
 
4
4
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
5
5
  import {
6
- GestureHandlerRootView,
7
6
  NativeViewGestureHandler,
8
7
  PanGestureHandler,
9
8
  State,
@@ -357,15 +356,10 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
357
356
  };
358
357
  }, [playerAnimationState, isAudioItem, isMinimizedModal]);
359
358
 
360
- const Wrapper = React.useMemo(
361
- () => (Platform.OS === "android" ? GestureHandlerRootView : View),
362
- []
363
- );
364
-
365
359
  const scrollEnabled = isMaximizedModal && isNotMinimizeMaximazeAnimation;
366
360
 
367
361
  return (
368
- <Wrapper style={generalStyles.container}>
362
+ <View style={generalStyles.container}>
369
363
  <TapGestureHandler
370
364
  maxDurationMs={100000}
371
365
  ref={tapHandlerRef}
@@ -405,7 +399,7 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
405
399
  </PanGestureHandler>
406
400
  </View>
407
401
  </TapGestureHandler>
408
- </Wrapper>
402
+ </View>
409
403
  );
410
404
  };
411
405
 
@@ -0,0 +1,294 @@
1
+ import React from "react";
2
+ import { Animated, StyleSheet, View } from "react-native";
3
+
4
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
5
+
6
+ import {
7
+ PlayerAnimationStateEnum,
8
+ useModalAnimationContext,
9
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
10
+ import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
11
+ import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
12
+
13
+ import {
14
+ resetScrollAnimatedValues,
15
+ setScrollModalAnimatedValue,
16
+ } from "./utils";
17
+
18
+ import { DURATION_TO_MINIMIZE } from "./const";
19
+
20
+ const getAnimatedConfig = (toValue) => {
21
+ return {
22
+ toValue,
23
+ duration: DURATION_TO_MINIMIZE,
24
+ useNativeDriver: true,
25
+ };
26
+ };
27
+
28
+ const generalStyles = StyleSheet.create({
29
+ container: {
30
+ flex: 1,
31
+ },
32
+ });
33
+
34
+ type Props = {
35
+ children: React.ReactNode;
36
+ };
37
+
38
+ export const AnimatedScrollModalComponent = ({ children }: Props) => {
39
+ const {
40
+ isActiveGesture,
41
+ playerAnimationState,
42
+ setPlayerAnimationState,
43
+ resetPlayerAnimationState,
44
+ animatedValues: {
45
+ lastScrollY,
46
+ dragScrollY,
47
+ dragVideoPlayerY,
48
+ translateYOffset,
49
+ },
50
+ lastScrollYValue,
51
+ scrollPosition,
52
+ modalSnapPoints,
53
+ setLastSnap,
54
+ setStartComponentsAnimation,
55
+ } = useModalAnimationContext();
56
+
57
+ const [enableGesture, setIEnableGesture] = React.useState<boolean>(true);
58
+
59
+ const { maximiseVideoModal, minimiseVideoModal, videoModalState } =
60
+ useNavigation();
61
+
62
+ const {
63
+ mode: videoModalMode,
64
+ previousMode: previousVideoModalMode,
65
+ item: videoModalItem,
66
+ } = videoModalState;
67
+
68
+ const isMaximizedModal: boolean = videoModalMode === "MAXIMIZED";
69
+ const isMinimizedModal: boolean = videoModalMode === "MINIMIZED";
70
+ const previousItemId = usePrevious(videoModalItem?.id);
71
+
72
+ const isNotMinimizeMaximazeAnimation =
73
+ playerAnimationState !== PlayerAnimationStateEnum.minimize &&
74
+ playerAnimationState !== PlayerAnimationStateEnum.maximize;
75
+
76
+ const isAudioItem = React.useMemo(
77
+ () =>
78
+ videoModalItem?.content?.type?.includes?.("audio") ||
79
+ videoModalItem?.type?.value === "audio",
80
+ [videoModalItem]
81
+ );
82
+
83
+ const onRegisterLastScroll = Animated.event(
84
+ [{ nativeEvent: { contentOffset: { y: lastScrollY } } }],
85
+ { useNativeDriver: true }
86
+ );
87
+
88
+ const onScroll = React.useCallback(({ nativeEvent }) => {
89
+ scrollPosition.current = nativeEvent.contentOffset.y;
90
+ }, []);
91
+
92
+ // Workaround for onMomentumScrollEnd issue
93
+ // https://github.com/facebook/react-native/issues/32696#issuecomment-1104217223
94
+ const canMomentum = React.useRef(false);
95
+
96
+ const onMomentumScrollBegin = React.useCallback(() => {
97
+ canMomentum.current = true;
98
+ }, []);
99
+
100
+ const onMomentumScrollEnd = React.useCallback(
101
+ ({ nativeEvent }) => {
102
+ if (canMomentum.current && !isActiveGesture) {
103
+ if (nativeEvent.contentOffset.y === 0) {
104
+ resetScrollAnimatedValues(
105
+ lastScrollY,
106
+ lastScrollYValue,
107
+ dragScrollY,
108
+ dragVideoPlayerY
109
+ );
110
+
111
+ setIEnableGesture(true);
112
+ } else {
113
+ setIEnableGesture(false);
114
+ }
115
+
116
+ canMomentum.current = false;
117
+ }
118
+ },
119
+ [isActiveGesture]
120
+ );
121
+
122
+ React.useEffect(() => {
123
+ return () => {
124
+ scrollPosition.current = 0;
125
+
126
+ resetScrollAnimatedValues(
127
+ lastScrollY,
128
+ lastScrollYValue,
129
+ dragScrollY,
130
+ dragVideoPlayerY
131
+ );
132
+ };
133
+ }, []);
134
+
135
+ React.useEffect(() => {
136
+ if (
137
+ videoModalMode === "MAXIMIZED" &&
138
+ !enableGesture &&
139
+ scrollPosition.current === 0
140
+ ) {
141
+ setIEnableGesture(true);
142
+ }
143
+
144
+ if (
145
+ videoModalMode === "MINIMIZED" &&
146
+ previousVideoModalMode === "MAXIMIZED"
147
+ ) {
148
+ // set animation to the minimize values if moving from the player to another screen
149
+ if (playerAnimationState === null) {
150
+ setScrollModalAnimatedValue(
151
+ translateYOffset,
152
+ modalSnapPoints[1],
153
+ setLastSnap
154
+ );
155
+
156
+ resetScrollAnimatedValues(
157
+ lastScrollY,
158
+ lastScrollYValue,
159
+ dragScrollY,
160
+ dragVideoPlayerY
161
+ );
162
+ }
163
+ } else if (
164
+ playerAnimationState === null &&
165
+ ((previousItemId === videoModalItem?.id &&
166
+ videoModalMode === "MAXIMIZED" &&
167
+ (previousVideoModalMode === "MINIMIZED" ||
168
+ previousVideoModalMode === "MAXIMIZED")) ||
169
+ (previousItemId !== videoModalItem?.id &&
170
+ videoModalMode !== "FULLSCREEN"))
171
+ ) {
172
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximize);
173
+ }
174
+ }, [videoModalMode, previousVideoModalMode, videoModalItem]);
175
+
176
+ React.useEffect(() => {
177
+ if (playerAnimationState === PlayerAnimationStateEnum.minimize) {
178
+ if (
179
+ (scrollPosition.current === 0 &&
180
+ (lastScrollY as any)._value !== 0 &&
181
+ (dragScrollY as any)._value === 0 &&
182
+ (dragVideoPlayerY as any)._value === 0) ||
183
+ (scrollPosition.current !== 0 &&
184
+ ((dragScrollY as any)._value !== 0 ||
185
+ (dragVideoPlayerY as any)._value !== 0))
186
+ ) {
187
+ resetScrollAnimatedValues(
188
+ lastScrollY,
189
+ lastScrollYValue,
190
+ dragScrollY,
191
+ dragVideoPlayerY
192
+ );
193
+ }
194
+
195
+ Animated.timing(
196
+ translateYOffset,
197
+ getAnimatedConfig(modalSnapPoints[1])
198
+ ).start(() => {
199
+ minimiseVideoModal();
200
+
201
+ setScrollModalAnimatedValue(
202
+ translateYOffset,
203
+ modalSnapPoints[1],
204
+ setLastSnap
205
+ );
206
+
207
+ resetScrollAnimatedValues(
208
+ lastScrollY,
209
+ lastScrollYValue,
210
+ dragScrollY,
211
+ dragVideoPlayerY
212
+ );
213
+
214
+ resetPlayerAnimationState();
215
+ });
216
+ } else if (playerAnimationState === PlayerAnimationStateEnum.maximize) {
217
+ Animated.timing(translateYOffset, getAnimatedConfig(0)).start(() => {
218
+ maximiseVideoModal();
219
+ setScrollModalAnimatedValue(translateYOffset, 0, setLastSnap);
220
+
221
+ resetScrollAnimatedValues(
222
+ lastScrollY,
223
+ lastScrollYValue,
224
+ dragScrollY,
225
+ dragVideoPlayerY
226
+ );
227
+
228
+ resetPlayerAnimationState();
229
+ });
230
+ }
231
+ }, [playerAnimationState]);
232
+
233
+ React.useEffect(() => {
234
+ const lastScrollYListenerId = lastScrollY.addListener(({ value }) => {
235
+ lastScrollYValue.current = value;
236
+ });
237
+
238
+ const dragListenerId = dragScrollY.addListener(({ value }) => {
239
+ const preparedValue =
240
+ isMinimizedModal && value >= 0
241
+ ? 0
242
+ : Math.round(isAudioItem ? Math.abs(value) : value);
243
+
244
+ if (
245
+ preparedValue > 0 &&
246
+ scrollPosition.current === 0 &&
247
+ playerAnimationState !== PlayerAnimationStateEnum.drag_player &&
248
+ playerAnimationState !== PlayerAnimationStateEnum.drag_scroll
249
+ ) {
250
+ isMinimizedModal && setStartComponentsAnimation(true);
251
+ setPlayerAnimationState(PlayerAnimationStateEnum.drag_scroll);
252
+ }
253
+ });
254
+
255
+ return () => {
256
+ lastScrollY.removeListener(lastScrollYListenerId);
257
+ dragScrollY.removeListener(dragListenerId);
258
+ };
259
+ }, [playerAnimationState, isAudioItem, isMinimizedModal]);
260
+
261
+ const scrollEnabled = isMaximizedModal && isNotMinimizeMaximazeAnimation;
262
+
263
+ return (
264
+ <View style={generalStyles.container}>
265
+ <View pointerEvents="box-none">
266
+ <Animated.View>
267
+ <Animated.ScrollView
268
+ scrollEnabled={scrollEnabled}
269
+ bounces={false}
270
+ onScrollBeginDrag={onRegisterLastScroll}
271
+ onScroll={onScroll}
272
+ onMomentumScrollBegin={onMomentumScrollBegin}
273
+ onMomentumScrollEnd={onMomentumScrollEnd}
274
+ scrollEventThrottle={1}
275
+ showsVerticalScrollIndicator={false}
276
+ >
277
+ {children}
278
+ </Animated.ScrollView>
279
+ </Animated.View>
280
+ </View>
281
+ </View>
282
+ );
283
+ };
284
+
285
+ export const AnimatedScrollModal = ({ children }: Props) => {
286
+ const {
287
+ videoModalState: { visible },
288
+ } = useNavigation();
289
+
290
+ const Component =
291
+ !isTV() && visible ? AnimatedScrollModalComponent : React.Fragment;
292
+
293
+ return <Component>{children}</Component>;
294
+ };