@applicaster/zapp-react-native-ui-components 12.0.0-alpha.7623722215 → 12.0.0-alpha.7754333813

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 (35) hide show
  1. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  2. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  3. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  4. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +105 -80
  5. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  6. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  7. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  8. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  9. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  10. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  11. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  12. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  13. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  14. package/Components/PlayerContainer/PlayerContainer.tsx +9 -5
  15. package/Components/River/ComponentsMap/ComponentsMap.tsx +86 -22
  16. package/Components/River/RefreshControl.tsx +55 -1
  17. package/Components/River/TV/River.tsx +2 -1
  18. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +4 -0
  19. package/Components/Screen/index.tsx +35 -24
  20. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +0 -5
  21. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +11 -4
  22. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +9 -2
  23. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +13 -2
  24. package/Components/VideoModal/PlayerWrapper.tsx +2 -2
  25. package/Components/VideoModal/VideoModal.tsx +19 -19
  26. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +1 -1
  27. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  28. package/Components/Viewport/utils.js +1 -1
  29. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  30. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  31. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  32. package/Contexts/ScreenContext/index.tsx +94 -25
  33. package/events/index.ts +1 -0
  34. package/package.json +5 -5
  35. /package/Contexts/{index.js → index.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
- import { View, FlatList, StyleSheet, Platform } from "react-native";
3
+ import { View, StyleSheet, Platform, FlatList } from "react-native";
4
4
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
5
5
  import { RiverItem } from "../RiverItem";
6
6
  import { RiverFooter } from "../RiverFooter";
@@ -21,7 +21,8 @@ import {
21
21
  import { useScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenData";
22
22
  import { isLast } from "@applicaster/zapp-react-native-utils/arrayUtils";
23
23
  import { withComponentsMapProvider } from "@applicaster/zapp-react-native-ui-components/Decorators/ComponentsMapWrapper";
24
- import { useScreenScrollStore } from "@applicaster/zapp-react-native-utils/screenState";
24
+ import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
25
+ import { useShallow } from "zustand/react/shallow";
25
26
 
26
27
  type Props = {
27
28
  feed: ZappFeed;
@@ -41,8 +42,10 @@ const styles = StyleSheet.create({
41
42
  },
42
43
  });
43
44
 
44
- const scrollIndicatorInsets = { right: 1 };
45
+ const getNearestValue = (value, optionA, optionB) =>
46
+ Math.abs(value - optionA) < Math.abs(value - optionB) ? optionA : optionB;
45
47
 
48
+ const scrollIndicatorInsets = { right: 1 };
46
49
  const keyExtractor = R.compose(String, R.prop("id"));
47
50
 
48
51
  function ComponentsMapComponent(props: Props) {
@@ -60,6 +63,7 @@ function ComponentsMapComponent(props: Props) {
60
63
  refreshingPipesV1,
61
64
  } = props;
62
65
 
66
+ const flatListRef = React.useRef<FlatList | null>(null);
63
67
  const screenConfig = useScreenConfiguration(riverId);
64
68
  const screenData = useScreenData(riverId);
65
69
  const pullToRefreshEnabled = screenData?.rules?.pull_to_refresh_enabled;
@@ -194,26 +198,81 @@ function ComponentsMapComponent(props: Props) {
194
198
  []
195
199
  );
196
200
 
197
- const onScrollFlatList = React.useCallback(
198
- ({ nativeEvent }, isScrollingDone = false) => {
199
- const {
200
- contentOffset: { y },
201
- } = nativeEvent;
201
+ const navBarStore = useScreenContextV2()._navBarStore;
202
202
 
203
- useScreenScrollStore.setState({
204
- scrollingVelocity: isScrollingDone ? 0 : 1,
205
- scrollPosition: y,
206
- });
207
- },
208
- []
203
+ const scrollY = navBarStore(
204
+ useShallow(({ scrollYAnimated }) => scrollYAnimated)
209
205
  );
210
206
 
211
- const onMomentumScrollEnd = React.useCallback(
212
- ({ nativeEvent }) => {
213
- onScrollFlatList({ nativeEvent }, true);
214
- },
215
- [onScrollFlatList]
216
- );
207
+ const canMomentum = React.useRef(false);
208
+
209
+ const onScrollBeginDrag = React.useCallback(() => {
210
+ canMomentum.current = true;
211
+ }, []);
212
+
213
+ /* onMomentumScrollEnd: Handling snap to expanded/collapsed state ( for navbar ) */
214
+ const _onMomentumScrollEnd = React.useCallback(({ nativeEvent }) => {
215
+ if (!canMomentum.current) return;
216
+ canMomentum.current = false;
217
+
218
+ const { height: headerHeight, scrollState } = navBarStore.getState();
219
+
220
+ const offsetY = Math.max(0, nativeEvent.contentOffset.y);
221
+
222
+ if (
223
+ !(scrollState === 0 || scrollState === -headerHeight) &&
224
+ flatListRef.current
225
+ ) {
226
+ const offset =
227
+ getNearestValue(Math.round(scrollState), -headerHeight, 0) ===
228
+ -headerHeight
229
+ ? Math.round(offsetY + scrollState + headerHeight)
230
+ : Math.round(offsetY + scrollState);
231
+
232
+ flatListRef.current.scrollToOffset({
233
+ animated: true,
234
+ offset,
235
+ });
236
+ }
237
+ }, []);
238
+
239
+ /* onScrollEnd: Handling snap to expanded/collapsed state ( for navbar ) */
240
+ const _onScrollEndDrag = React.useCallback(({ nativeEvent }) => {
241
+ const { height: headerHeight, scrollState } = navBarStore.getState();
242
+
243
+ const offsetY = Math.max(0, nativeEvent.contentOffset.y);
244
+
245
+ if (
246
+ !(scrollState === 0 || scrollState === -headerHeight) &&
247
+ flatListRef.current &&
248
+ nativeEvent.velocity.y === 0
249
+ ) {
250
+ const offset =
251
+ getNearestValue(Math.round(scrollState), -headerHeight, 0) ===
252
+ -headerHeight
253
+ ? Math.round(offsetY + scrollState + headerHeight)
254
+ : Math.round(offsetY + scrollState);
255
+
256
+ flatListRef.current.scrollToOffset({
257
+ animated: true,
258
+ offset,
259
+ });
260
+ }
261
+ }, []);
262
+
263
+ const onScroll = React.useCallback((event) => {
264
+ const {
265
+ nativeEvent: {
266
+ contentOffset: { y },
267
+ layoutMeasurement: { height },
268
+ contentSize: { height: contentHeight },
269
+ },
270
+ } = event;
271
+
272
+ if (y >= 0 && y + height <= contentHeight) {
273
+ scrollY.setValue(y);
274
+ }
275
+ }, []);
217
276
 
218
277
  if (shouldShowLoadingError) {
219
278
  return <RiverError />;
@@ -230,6 +289,9 @@ function ComponentsMapComponent(props: Props) {
230
289
  >
231
290
  <ViewportTracker>
232
291
  <FlatList
292
+ ref={(ref) => {
293
+ flatListRef.current = ref;
294
+ }}
233
295
  // Fix for WebView rerender crashes on Android API 28+
234
296
  // https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
235
297
  overScrollMode={Platform.OS === "android" ? "never" : "auto"}
@@ -249,8 +311,10 @@ function ComponentsMapComponent(props: Props) {
249
311
  }
250
312
  ListFooterComponent={renderFooter}
251
313
  refreshControl={refreshControl}
252
- onScroll={onScrollFlatList}
253
- onMomentumScrollEnd={onMomentumScrollEnd}
314
+ onScrollBeginDrag={onScrollBeginDrag}
315
+ onScroll={onScroll}
316
+ onMomentumScrollEnd={_onMomentumScrollEnd}
317
+ onScrollEndDrag={_onScrollEndDrag}
254
318
  scrollEventThrottle={16}
255
319
  {...scrollViewExtraProps}
256
320
  />
@@ -1,5 +1,9 @@
1
1
  import * as React from "react";
2
- import { RefreshControl as RNRefreshControl } from "react-native";
2
+ import {
3
+ Platform,
4
+ RefreshControl as RNRefreshControl,
5
+ StyleSheet,
6
+ } from "react-native";
3
7
  import * as R from "ramda";
4
8
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
5
9
  import { useLocalizedStrings } from "@applicaster/zapp-react-native-utils/localizationUtils";
@@ -8,10 +12,30 @@ import { useSendAnalyticsEventWithFunction } from "@applicaster/zapp-react-nativ
8
12
  import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
9
13
  import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
10
14
  import { useDispatch } from "react-redux";
15
+ import { useShallow } from "zustand/react/shallow";
16
+ import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
17
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
11
18
 
12
19
  const BRIGHTNESS_THRESHOLD = 160;
13
20
  const ABOVE_DEFAULT_COLOR = "gray";
14
21
  const BELOW_DEFAULT_COLOR = "white";
22
+ const BASE_INDICATOR_HEIGHT = 40;
23
+ const BASE_INDICATOR_OFFSET = 16;
24
+
25
+ /** Return spacing required to position indicator below the navbar bottom line (Android only) */
26
+ const getRefreshControlOffset = () => {
27
+ return Platform.OS === "ios"
28
+ ? 0
29
+ : BASE_INDICATOR_HEIGHT + BASE_INDICATOR_OFFSET;
30
+ };
31
+
32
+ const styles = StyleSheet.create({
33
+ // eslint-disable-next-line react-native/no-color-literals
34
+ control: {
35
+ zIndex: 1,
36
+ backgroundColor: "transparent",
37
+ },
38
+ });
15
39
 
16
40
  const getBrightness = (RGBcolor) => {
17
41
  const RGBvalues = RGBcolor.substring(
@@ -91,6 +115,34 @@ export const usePullToRefresh = (
91
115
  };
92
116
  };
93
117
 
118
+ /** Returns the offset for the progress view of the RefreshControl component
119
+ * based on navbar content position */
120
+ export const useGetProgressViewOffset = () => {
121
+ const { _navBarStore } = useScreenContextV2();
122
+ const { top } = useSafeAreaInsets();
123
+
124
+ const offset = getRefreshControlOffset();
125
+
126
+ const { height, contentPosition } = _navBarStore(
127
+ useShallow(({ contentPosition, height }) => ({ contentPosition, height }))
128
+ );
129
+
130
+ if (height === 0) {
131
+ return offset;
132
+ }
133
+
134
+ switch (contentPosition) {
135
+ case "on_bottom_status_bar":
136
+ // navbar height
137
+ return offset + height - top;
138
+ case "on_top_screen":
139
+ // navbar height + status bar height
140
+ return offset + height;
141
+ default:
142
+ return offset;
143
+ }
144
+ };
145
+
94
146
  export function RefreshControl(props: {
95
147
  pullToRefreshPipesV1RefreshingStateUpdater?: (refreshing: boolean) => void;
96
148
  refreshingPipesV1?: boolean;
@@ -211,6 +263,7 @@ export function RefreshControl(props: {
211
263
  refreshing={refreshing}
212
264
  onRefresh={onRefreshHandler}
213
265
  colors={[preparedIndicatorColor]}
266
+ progressViewOffset={useGetProgressViewOffset()}
214
267
  progressBackgroundColor={preparedIndicatorBackgroundColor}
215
268
  // @ts-ignore in RN68 this `size` became string
216
269
  size={indicatorSize}
@@ -222,6 +275,7 @@ export function RefreshControl(props: {
222
275
  : null
223
276
  }
224
277
  titleColor={preparedTitleUnderIndicatorColor}
278
+ style={styles.control}
225
279
  />
226
280
  );
227
281
  }
@@ -106,7 +106,8 @@ export const River = (props: Props) => {
106
106
  navigationPluginData?.rules?.focus_on_content
107
107
  ) &&
108
108
  river &&
109
- river.type !== "general_content"
109
+ river.type !== "general_content" &&
110
+ river.type !== "tabs_screen" // Tabs screen is kind of general content
110
111
  ) {
111
112
  const groupId = R.pathOr("river", ["groupId"], componentsMapExtraProps);
112
113
 
@@ -11,7 +11,9 @@ exports[`<Screen Component /> when the navbar should be hidden renders correctly
11
11
  }
12
12
  >
13
13
  <View
14
+ hasMenu={false}
14
15
  id="/river/testId"
16
+ pathname="/river/testId"
15
17
  selected="testId"
16
18
  testID="navBar"
17
19
  title=""
@@ -41,7 +43,9 @@ exports[`<Screen Component /> when the navbar should show renders correctly 1`]
41
43
  }
42
44
  >
43
45
  <View
46
+ hasMenu={false}
44
47
  id="/river/testId"
48
+ pathname="/river/testId"
45
49
  selected="testId"
46
50
  testID="navBar"
47
51
  title=""
@@ -6,7 +6,10 @@ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
6
6
 
7
7
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
8
8
  import { getComponentModule } from "@applicaster/zapp-react-native-utils/pluginUtils";
9
- import { getNavigationPropsV2 } from "@applicaster/zapp-react-native-utils/navigationUtils";
9
+ import {
10
+ getNavigationPropsV2,
11
+ resolveNavigationPlugin,
12
+ } from "@applicaster/zapp-react-native-utils/navigationUtils";
10
13
  import { useSafeAreaInsets } from "react-native-safe-area-context";
11
14
  import {
12
15
  useDimensions,
@@ -52,11 +55,11 @@ function isValidColor(string) {
52
55
  }
53
56
 
54
57
  type Props = {
55
- setToggleMenu: () => void;
56
- setCloseMenu: () => void;
58
+ pathname: unknown;
59
+ screenData: unknown;
57
60
  };
58
61
 
59
- export function Screen({ setToggleMenu, setCloseMenu }: Props) {
62
+ export function Screen(_props: Props) {
60
63
  const theme = useTheme<BaseThemePropertiesMobile>();
61
64
  const navigation = useNavigation();
62
65
  const { plugins } = usePickFromState(["plugins"]);
@@ -70,26 +73,38 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
70
73
  fullDimensions: true,
71
74
  });
72
75
 
73
- const routeData = useRoute();
76
+ const { screenData, pathname } = useRoute();
74
77
 
75
78
  const currentRiver = useScreenData(
76
- getScreenId(routeData.screenData, navigation.activeRiver)
79
+ getScreenId(screenData, navigation.activeRiver)
77
80
  );
78
81
 
79
82
  const { title } = useNavbarState();
80
83
 
81
- const navBarProps = React.useMemo(
82
- () => ({
83
- ...getNavigationPropsV2({
84
- currentRiver,
85
- title,
86
- category: "nav_bar",
87
- }),
88
- setToggleMenu,
89
- setCloseMenu,
90
- }),
91
- [currentRiver, setCloseMenu, setToggleMenu]
92
- );
84
+ const hasMenu =
85
+ resolveNavigationPlugin({
86
+ category: "menu",
87
+ navigations: currentRiver.navigations,
88
+ plugins,
89
+ })?.identifier === "quick_brick_side_menu";
90
+
91
+ const navBarProps = React.useMemo<MobileNavBarPluginProps | null>(() => {
92
+ const props = getNavigationPropsV2({
93
+ currentRiver,
94
+ title,
95
+ category: "nav_bar",
96
+ });
97
+
98
+ if (props) {
99
+ return {
100
+ ...props,
101
+ id: pathname,
102
+ pathname: pathname,
103
+ };
104
+ }
105
+
106
+ return null;
107
+ }, [currentRiver, pathname]);
93
108
 
94
109
  const NavBar = React.useMemo(
95
110
  () =>
@@ -97,12 +112,10 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
97
112
  "nav_bar",
98
113
  currentRiver,
99
114
  plugins
100
- ) as React.ComponentType<MobileNavBarPluginProps>,
115
+ ) as React.ComponentType<MobileNavBarPluginProps & { hasMenu: boolean }>,
101
116
  []
102
117
  );
103
118
 
104
- const { screenData, pathname } = routeData;
105
-
106
119
  const offlinePlugin = getComponentModule({
107
120
  plugins,
108
121
  components: {},
@@ -133,9 +146,7 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
133
146
  return (
134
147
  <ScreenContextProvider pathname={pathname}>
135
148
  <View style={style}>
136
- <NavBar
137
- {...{ ...(navBarProps as MobileNavBarPluginProps), id: pathname }}
138
- />
149
+ {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
139
150
 
140
151
  <OfflineFallbackScreen screenData={targetScreenData}>
141
152
  {/* @TODO RouteManager doesn't use props, can they be removed ? */}
@@ -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>
@@ -9,6 +9,8 @@ import {
9
9
  TapGestureHandler,
10
10
  GestureHandlerRootView,
11
11
  } from "react-native-gesture-handler";
12
+
13
+ import { PlayerContainerContext } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainerContext";
12
14
  import {
13
15
  useModalAnimationContext,
14
16
  PlayerAnimationStateEnum,
@@ -58,6 +60,8 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
58
60
  setLastSnap,
59
61
  } = useModalAnimationContext();
60
62
 
63
+ const { isLanguageOverlayVisible } = React.useContext(PlayerContainerContext);
64
+
61
65
  const { maximiseVideoModal, minimiseVideoModal, videoModalState } =
62
66
  useNavigation();
63
67
 
@@ -69,6 +73,11 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
69
73
  playerAnimationState !== PlayerAnimationStateEnum.minimize &&
70
74
  playerAnimationState !== PlayerAnimationStateEnum.maximaze;
71
75
 
76
+ const isEnablePanGesture =
77
+ !isLanguageOverlayVisible &&
78
+ isNotMinimizeMaximazeAnimation &&
79
+ (isMaximazedModal || isMinimizedModal);
80
+
72
81
  const isAudioItem = React.useMemo(
73
82
  () =>
74
83
  videoModalState.item?.content?.type?.includes?.("audio") ||
@@ -290,15 +299,13 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
290
299
  >
291
300
  <View pointerEvents="box-none">
292
301
  <PanGestureHandler
293
- enabled={
294
- (isMaximazedModal || isMinimizedModal) &&
295
- isNotMinimizeMaximazeAnimation
296
- }
302
+ enabled={isEnablePanGesture}
297
303
  ref={panHandlerRef}
298
304
  simultaneousHandlers={[scrollRef, tapHandlerRef]}
299
305
  shouldCancelWhenOutside={isMaximazedModal}
300
306
  onGestureEvent={onGestureEvent}
301
307
  onHandlerStateChange={onHandlerStateChange}
308
+ activeOffsetY={[-5, 5]}
302
309
  >
303
310
  <Animated.View>
304
311
  <NativeViewGestureHandler
@@ -3,6 +3,8 @@ import { Animated, StyleSheet } from "react-native";
3
3
 
4
4
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
5
5
  import { PanGestureHandler, State } from "react-native-gesture-handler";
6
+
7
+ import { PlayerContainerContext } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainerContext";
6
8
  import {
7
9
  useModalAnimationContext,
8
10
  PlayerAnimationStateEnum,
@@ -35,9 +37,14 @@ export const AnimatedVideoPlayer = ({ children }: Props) => {
35
37
  } = useModalAnimationContext();
36
38
 
37
39
  const { videoModalState } = useNavigation();
40
+ const { isLanguageOverlayVisible } = React.useContext(PlayerContainerContext);
41
+
38
42
  const isMaximazedModal = videoModalState.mode === "MAXIMIZED";
39
43
  const isMinimizedModal = videoModalState.mode === "MINIMIZED";
40
44
 
45
+ const isEnablePanGesture =
46
+ !isLanguageOverlayVisible && (isMaximazedModal || isMinimizedModal);
47
+
41
48
  const onGestureEvent = Animated.event(
42
49
  [{ nativeEvent: { translationY: dragVideoPlayerY } }],
43
50
  { useNativeDriver: true }
@@ -53,7 +60,7 @@ export const AnimatedVideoPlayer = ({ children }: Props) => {
53
60
  if (videoModalState.mode === "MAXIMIZED") {
54
61
  if (translationY > 0) {
55
62
  const endOffsetY =
56
- lastSnap + preparedTranslationY + dragToss * velocityY + 150;
63
+ lastSnap + preparedTranslationY + dragToss * velocityY + 200;
57
64
 
58
65
  let destSnapPoint = modalSnapPoints[0];
59
66
 
@@ -127,7 +134,7 @@ export const AnimatedVideoPlayer = ({ children }: Props) => {
127
134
 
128
135
  return (
129
136
  <PanGestureHandler
130
- enabled={isMaximazedModal || isMinimizedModal}
137
+ enabled={isEnablePanGesture}
131
138
  shouldCancelWhenOutside={isMaximazedModal}
132
139
  onGestureEvent={onGestureEvent}
133
140
  onHandlerStateChange={onHandlerStateChange}
@@ -1,7 +1,10 @@
1
1
  import React, { useContext } from "react";
2
2
  import { Animated } from "react-native";
3
3
 
4
- import { useSafeAreaFrame } from "react-native-safe-area-context";
4
+ import {
5
+ useSafeAreaFrame,
6
+ useSafeAreaInsets,
7
+ } from "react-native-safe-area-context";
5
8
  import { useGetBottomTabBarHeight } from "@applicaster/zapp-react-native-utils/reactHooks";
6
9
 
7
10
  import { useConfiguration } from "../utils";
@@ -91,8 +94,16 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
91
94
  const dragVideoPlayerY = React.useRef(new Animated.Value(0)).current;
92
95
 
93
96
  const { height } = useSafeAreaFrame();
97
+ const { bottom: bottomSafeArea } = useSafeAreaInsets();
94
98
  const bottomTabBarHeight = useGetBottomTabBarHeight();
95
- const minValue = height - minimisedHeight - bottomTabBarHeight;
99
+
100
+ /*
101
+ If bottomTabBarHeight is equal 0 it means an app does not use bottomTabBar.
102
+ Because of this we need to minus bottom SafeArea offset.
103
+ */
104
+ const minValue =
105
+ height - minimisedHeight - (bottomTabBarHeight || bottomSafeArea);
106
+
96
107
  const modalSnapPoints = React.useMemo(() => [0, minValue], [minValue]);
97
108
  // Last snap state which will helps us to make smooth responder to scrollview animation
98
109
  const [lastSnap, setLastSnap] = React.useState<number>(modalSnapPoints[0]);
@@ -96,7 +96,7 @@ const getEdges = (isTablet: boolean, isInlineModal: boolean) => {
96
96
  }
97
97
 
98
98
  if (!isInlineModal && Platform.OS === "android") {
99
- return [];
99
+ return ["top", "left", "right", "bottom"];
100
100
  }
101
101
 
102
102
  return ["top"];
@@ -161,7 +161,7 @@ export const PlayerWrapper = (props: Props) => {
161
161
  width: isInlineModal && docked ? undefined : baseDimensions.width,
162
162
  aspectRatio: !isInlineModal && !pip ? getScreenAspectRatio() : 16 / 9,
163
163
  }),
164
- [baseDimensions, isInlineModal, pip]
164
+ [baseDimensions, isInlineModal, pip, docked]
165
165
  );
166
166
 
167
167
  const containerDimensions: DimensionsT = React.useMemo(
@@ -129,17 +129,17 @@ const VideoModalComponent = () => {
129
129
  const pathname = `${ROUTE_TYPES.VIDEO_MODAL}/${item?.id}`;
130
130
 
131
131
  return (
132
- <AnimatedPlayerModalWrapper
133
- style={[
134
- styles.container,
135
- {
136
- backgroundColor,
137
- },
138
- ]}
139
- >
140
- <ScreenContextProvider pathname={pathname}>
141
- <PathnameContext.Provider value={pathname}>
142
- {itemIdHooksFinished === item?.id ? (
132
+ <ScreenContextProvider pathname={pathname}>
133
+ <PathnameContext.Provider value={pathname}>
134
+ {itemIdHooksFinished === item?.id ? (
135
+ <AnimatedPlayerModalWrapper
136
+ style={[
137
+ styles.container,
138
+ {
139
+ backgroundColor,
140
+ },
141
+ ]}
142
+ >
143
143
  <HandlePlayable
144
144
  item={item}
145
145
  isModal={mode !== "PIP"}
@@ -147,14 +147,14 @@ const VideoModalComponent = () => {
147
147
  modalWidth={modalSize.width}
148
148
  mode={mode}
149
149
  />
150
- ) : (
151
- <View style={styles.loaderContainer}>
152
- <Spinner />
153
- </View>
154
- )}
155
- </PathnameContext.Provider>
156
- </ScreenContextProvider>
157
- </AnimatedPlayerModalWrapper>
150
+ </AnimatedPlayerModalWrapper>
151
+ ) : (
152
+ <View style={styles.loaderContainer}>
153
+ <Spinner />
154
+ </View>
155
+ )}
156
+ </PathnameContext.Provider>
157
+ </ScreenContextProvider>
158
158
  );
159
159
  };
160
160
 
@@ -15,7 +15,7 @@ export function ViewportTracker(props) {
15
15
 
16
16
  const assignRef = React.useCallback(
17
17
  (ref) => {
18
- callOnChildren(children, "ref", ref);
18
+ children?.ref?.(ref);
19
19
  setNodeHandle(findNodeHandle(ref));
20
20
  },
21
21
  [children]
@@ -6,6 +6,7 @@ import { ViewportEventContext, ViewportEvents } from "../ViewportEvents";
6
6
  type Props = {
7
7
  viewportEventsManager?: any;
8
8
  children: React.ReactNode;
9
+ flatListRef?: any; // this ref is used to bind to children component
9
10
  };
10
11
 
11
12
  export function ViewportTracker(props: Props) {
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
 
3
3
  export function callOnChildren(children, method, ...args) {
4
4
  const childMethod = React.Children.only(children).props[method];
5
- childMethod && childMethod(...args);
5
+ typeof childMethod === "function" && childMethod?.(...args);
6
6
  }
7
7
 
8
8
  export function isInViewport(
@@ -1,4 +1,5 @@
1
1
  import { createContext } from "@applicaster/zapp-react-native-utils/reactUtils/createContext";
2
+ import React from "react";
2
3
 
3
4
  const initialContext = { toggleMenu: {}, closeMenu: {}, openMenu: {} };
4
5
 
@@ -16,3 +17,18 @@ const stateValidator = ({ property }) =>
16
17
  property === "openMenu";
17
18
 
18
19
  export const MenuToggleContext = createContext(initialContext, stateValidator);
20
+
21
+ export type MenuToggleContextType = {
22
+ toggleMenu: any;
23
+ setToggleMenu: () => void;
24
+ openMenu: any;
25
+ setOpenMenu: () => void;
26
+ closeMenu: any;
27
+ setCloseMenu: () => void;
28
+ };
29
+
30
+ export const useMenuToggleContext = () => {
31
+ return React.useContext<MenuToggleContextType>(
32
+ MenuToggleContext.ReactContext
33
+ );
34
+ };
@@ -0,0 +1,3 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ScreenContext withScreenContext should return the context 1`] = `null`;
@@ -0,0 +1,27 @@
1
+ import { render } from "@testing-library/react-native";
2
+ import { ScreenContext, ScreenContextProvider, withScreenContext } from "../";
3
+ import React from "react";
4
+
5
+ describe("ScreenContext", () => {
6
+ describe("ScreneContext context", () => {
7
+ it("should return the context", () => {
8
+ expect(ScreenContext).toBeDefined();
9
+ });
10
+ });
11
+
12
+ describe("ScreenContextProvider", () => {
13
+ it("should return the provider", () => {
14
+ expect(ScreenContextProvider).toBeDefined();
15
+ });
16
+ });
17
+
18
+ describe("withScreenContext", () => {
19
+ it("should return the context", () => {
20
+ const Component = () => null;
21
+ const WrappedComponent = withScreenContext(Component);
22
+ const { toJSON } = render(<WrappedComponent />);
23
+
24
+ expect(toJSON()).toMatchSnapshot();
25
+ });
26
+ });
27
+ });