@applicaster/zapp-react-native-ui-components 12.0.0-alpha.4300857220 → 12.0.0-alpha.4337072244

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 (36) hide show
  1. package/Components/BaseFocusable/index.tsx +1 -1
  2. package/Components/Cell/Cell.tsx +5 -5
  3. package/Components/Cell/CellWithFocusable.tsx +17 -5
  4. package/Components/FocusableGroup/index.tsx +1 -0
  5. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  6. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  7. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  8. package/Components/Layout/TV/__tests__/index.test.tsx +18 -74
  9. package/Components/Layout/TV/index.tsx +43 -102
  10. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  11. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  12. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  13. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  14. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  15. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +4 -1
  16. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  17. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  18. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  19. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  20. package/Components/PlayerContainer/PlayerContainer.tsx +4 -1
  21. package/Components/River/ComponentsMap/ComponentsMap.tsx +18 -17
  22. package/Components/River/RefreshControl.tsx +55 -1
  23. package/Components/River/TV/River.tsx +0 -23
  24. package/Components/Screen/TV/hooks/index.ts +3 -0
  25. package/Components/Screen/TV/hooks/useInitialFocus.ts +67 -0
  26. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  27. package/Components/Screen/TV/index.web.tsx +174 -0
  28. package/Components/ScreenResolver/index.tsx +3 -1
  29. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +13 -2
  30. package/Components/VideoModal/PlayerWrapper.tsx +2 -2
  31. package/Components/VideoModal/VideoModal.tsx +19 -19
  32. package/Contexts/ScreenContext/index.tsx +2 -2
  33. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  34. package/events/index.ts +1 -0
  35. package/package.json +5 -5
  36. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
@@ -61,7 +61,10 @@ export function ButtonContainerView({
61
61
  <View style={applyRTLStylesIfNeeded(style, isRTL)}>
62
62
  {insertBetween(
63
63
  (index) => (
64
- <HorizontalSeparator key={index} width={horizontalGutter} />
64
+ <HorizontalSeparator
65
+ key={`separator_${index}`}
66
+ width={horizontalGutter}
67
+ />
65
68
  ),
66
69
  cloneElementsWithIds(buttonIds, filteredChildren)
67
70
  )}
@@ -29,7 +29,10 @@ export function ButtonContainerView({
29
29
  <View style={style}>
30
30
  {insertBetween(
31
31
  (index) => (
32
- <HorizontalSeparator key={index} width={horizontalGutter} />
32
+ <HorizontalSeparator
33
+ key={`separator_${index}`}
34
+ width={horizontalGutter}
35
+ />
33
36
  ),
34
37
  filteredChildren
35
38
  )}
@@ -51,9 +51,7 @@ export const CollapsibleTextContainer = ({
51
51
  func: (data) => {
52
52
  return getColorFromData({
53
53
  data,
54
- getValue,
55
- key: "background_color",
56
- defaultColor: additionalStyles.backgroundColor,
54
+ valueFromLayout: additionalStyles.backgroundColor,
57
55
  });
58
56
  },
59
57
  propName: "backgroundColor",
@@ -53,7 +53,7 @@ export const useAsyncRendering = (viewRef: React.RefObject<View>): Return => {
53
53
  measure(viewRef?.current, ({ width, height }) => {
54
54
  setDimensions({
55
55
  width,
56
- height,
56
+ height: Math.ceil(height),
57
57
  });
58
58
  });
59
59
  })
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { View, StyleSheet } from "react-native";
3
+ import { MeasurementPortalContext } from "./MeasurementsPortal";
3
4
 
4
5
  type Props = {
5
6
  Component: React.ElementType;
@@ -19,13 +20,16 @@ const _MeasurementsPortal = (
19
20
  { Component, onLayout, componentProps, containerStyle }: Props,
20
21
  ref
21
22
  ) => (
22
- <View
23
- onLayout={onLayout}
24
- style={[styles.container, containerStyle]}
25
- ref={ref}
26
- >
27
- <Component {...componentProps} />
28
- </View>
23
+ // hack for cell to determine if cell is in measurement mode
24
+ <MeasurementPortalContext.Provider value={{}}>
25
+ <View
26
+ onLayout={onLayout}
27
+ style={[styles.container, containerStyle]}
28
+ ref={ref}
29
+ >
30
+ <Component {...componentProps} />
31
+ </View>
32
+ </MeasurementPortalContext.Provider>
29
33
  );
30
34
 
31
35
  export const MeasurementsPortal = React.forwardRef(_MeasurementsPortal);
@@ -35,7 +35,16 @@ const MeasurementsPortal = ({ Component, onLayout, componentProps }: Props) => {
35
35
  );
36
36
  };
37
37
 
38
- const MeasurementPortalContext = React.createContext(null);
38
+ type MeasurementPortalContextType = {
39
+ measureComponent?: (
40
+ comp: React.ElementType,
41
+ props: Record<any, any>
42
+ ) => Promise<any>;
43
+ measuringInProgress?: boolean;
44
+ };
45
+
46
+ const MeasurementPortalContext =
47
+ React.createContext<null | MeasurementPortalContextType>(null);
39
48
 
40
49
  const MeasurementsPortalContextProvider = ({ children }) => {
41
50
  const Component = React.useRef(View);
@@ -68,6 +68,7 @@ type Props = {
68
68
  mode?: VideoModalMode;
69
69
  loadPipesData: (url: string, options: {}) => void;
70
70
  disableCastAction?: boolean;
71
+ groupId?: string;
71
72
  };
72
73
 
73
74
  type ScreenStyles = ZappRiver["styles"] & {
@@ -200,6 +201,7 @@ const PlayerContainerComponent = (props: Props) => {
200
201
  style: videoStyle,
201
202
  mode = VideoModalMode.MAXIMIZED,
202
203
  disableCastAction = false,
204
+ groupId,
203
205
  } = props;
204
206
 
205
207
  // Hooks
@@ -249,7 +251,6 @@ const PlayerContainerComponent = (props: Props) => {
249
251
  return;
250
252
  }
251
253
 
252
- navigator.goBack();
253
254
  showNavBar(true);
254
255
  }, [isModal, navigator.goBack, showNavBar]);
255
256
 
@@ -629,6 +630,7 @@ const PlayerContainerComponent = (props: Props) => {
629
630
  style={styles.focusableGroup}
630
631
  preferredFocus
631
632
  shouldUsePreferredFocus
633
+ groupId={groupId}
632
634
  >
633
635
  {/* Video player and components */}
634
636
  <View
@@ -671,6 +673,7 @@ const PlayerContainerComponent = (props: Props) => {
671
673
  setNextVideoPreloadThresholdPercentage={
672
674
  setNextVideoPreloadThresholdPercentage
673
675
  }
676
+ startComponentsAnimation={startComponentsAnimation}
674
677
  >
675
678
  {renderApplePlayer(applePlayerProps)}
676
679
  </Player>
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
- import { View, StyleSheet, Platform, Animated, FlatList } 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";
@@ -217,7 +217,7 @@ function ComponentsMapComponent(props: Props) {
217
217
 
218
218
  const { height: headerHeight, scrollState } = navBarStore.getState();
219
219
 
220
- const offsetY = nativeEvent.contentOffset.y;
220
+ const offsetY = Math.max(0, nativeEvent.contentOffset.y);
221
221
 
222
222
  if (
223
223
  !(scrollState === 0 || scrollState === -headerHeight) &&
@@ -240,7 +240,7 @@ function ComponentsMapComponent(props: Props) {
240
240
  const _onScrollEndDrag = React.useCallback(({ nativeEvent }) => {
241
241
  const { height: headerHeight, scrollState } = navBarStore.getState();
242
242
 
243
- const offsetY = nativeEvent.contentOffset.y;
243
+ const offsetY = Math.max(0, nativeEvent.contentOffset.y);
244
244
 
245
245
  if (
246
246
  !(scrollState === 0 || scrollState === -headerHeight) &&
@@ -260,6 +260,20 @@ function ComponentsMapComponent(props: Props) {
260
260
  }
261
261
  }, []);
262
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
+ }, []);
276
+
263
277
  if (shouldShowLoadingError) {
264
278
  return <RiverError />;
265
279
  }
@@ -298,20 +312,7 @@ function ComponentsMapComponent(props: Props) {
298
312
  ListFooterComponent={renderFooter}
299
313
  refreshControl={refreshControl}
300
314
  onScrollBeginDrag={onScrollBeginDrag}
301
- onScroll={Animated.event(
302
- [
303
- {
304
- nativeEvent: {
305
- contentOffset: {
306
- y: scrollY,
307
- },
308
- },
309
- },
310
- ],
311
- {
312
- useNativeDriver: false,
313
- }
314
- )}
315
+ onScroll={onScroll}
315
316
  onMomentumScrollEnd={_onMomentumScrollEnd}
316
317
  onScrollEndDrag={_onScrollEndDrag}
317
318
  scrollEventThrottle={16}
@@ -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
  }
@@ -7,14 +7,10 @@ import * as R from "ramda";
7
7
  import {
8
8
  useFeedLoader,
9
9
  useLayoutVersion,
10
- useNavigationPluginData,
11
10
  } from "@applicaster/zapp-react-native-utils/reactHooks";
12
11
  import { GeneralContentScreen } from "../../GeneralContentScreen";
13
12
  import { ScreenResolver } from "@applicaster/zapp-react-native-ui-components/Components/ScreenResolver";
14
13
  import { utilsLogger } from "@applicaster/zapp-react-native-utils/logger";
15
- import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils";
16
- import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
17
- import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
18
14
  import {
19
15
  useNavbarState,
20
16
  useSetNavbarState,
@@ -51,8 +47,6 @@ export const River = (props: Props) => {
51
47
  const { setTitle: setScreenTitle, setSummary: setScreenSummary } =
52
48
  useSetNavbarState();
53
49
 
54
- const navigationPluginData = useNavigationPluginData();
55
-
56
50
  const rivers = useRivers();
57
51
  const isV2 = useLayoutVersion({ isV2: true });
58
52
 
@@ -99,23 +93,6 @@ export const River = (props: Props) => {
99
93
  }
100
94
  }, [feedData, screenData, screenTitle, screenSummary]);
101
95
 
102
- React.useEffect(() => {
103
- if (
104
- isWeb() &&
105
- toBooleanWithDefaultFalse(
106
- navigationPluginData?.rules?.focus_on_content
107
- ) &&
108
- river &&
109
- river.type !== "general_content"
110
- ) {
111
- const groupId = R.pathOr("river", ["groupId"], componentsMapExtraProps);
112
-
113
- focusManager.setupInitialFocus(groupId, true);
114
- focusManager.setInitialFocus();
115
- focusManager.enableAutomaticFocus();
116
- }
117
- }, [river, componentsMapExtraProps]);
118
-
119
96
  if (!river) {
120
97
  utilsLogger.error({
121
98
  message: `River ${screenData.id} not found!`,
@@ -0,0 +1,3 @@
1
+ export { useInitialFocus } from "./useInitialFocus";
2
+
3
+ export { useRestoreFocus } from "./useRestoreFocus";
@@ -0,0 +1,67 @@
1
+ import * as React from "react";
2
+ import {
3
+ useNavigationPluginData,
4
+ useIsNavBarVisible,
5
+ useRoute,
6
+ } from "@applicaster/zapp-react-native-utils/reactHooks";
7
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
8
+ import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
9
+
10
+ type Return =
11
+ | {
12
+ onContent: true;
13
+ onMenu: false;
14
+ }
15
+ | {
16
+ onContent: false;
17
+ onMenu: true;
18
+ };
19
+
20
+ const getInitialFocus = (focusOnContent, isNavBarVisible): Return => {
21
+ if (focusOnContent) {
22
+ return {
23
+ onContent: true,
24
+ onMenu: false,
25
+ };
26
+ }
27
+
28
+ if (!focusOnContent && isNavBarVisible) {
29
+ // put focus on navbar
30
+ return {
31
+ onContent: false,
32
+ onMenu: true,
33
+ };
34
+ }
35
+
36
+ // we don't have navbar, put focus on content anyway
37
+ return {
38
+ onContent: true,
39
+ onMenu: false,
40
+ };
41
+ };
42
+
43
+ export const useInitialFocus = (): void => {
44
+ const navigationPluginData = useNavigationPluginData();
45
+
46
+ const focusOnContent = toBooleanWithDefaultFalse(
47
+ navigationPluginData?.rules?.focus_on_content
48
+ );
49
+
50
+ const isNavBarVisible = useIsNavBarVisible();
51
+
52
+ const { pathname: currentRoute } = useRoute();
53
+
54
+ React.useEffect(() => {
55
+ const initialFocus = getInitialFocus(focusOnContent, isNavBarVisible);
56
+
57
+ if (initialFocus.onContent) {
58
+ focusManager.setFocusOnContent(currentRoute);
59
+
60
+ return;
61
+ }
62
+
63
+ if (initialFocus.onMenu) {
64
+ focusManager.setFocusOnMenu(currentRoute);
65
+ }
66
+ }, []);
67
+ };
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ import { isNil } from "ramda";
3
+ import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
4
+ import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
5
+ import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
6
+
7
+ export const useRestoreFocus = (): void => {
8
+ const isScreenActive = useIsScreenActive();
9
+ const previousIsScreenActive = usePrevious(isScreenActive);
10
+
11
+ React.useEffect(() => {
12
+ if (isNil(previousIsScreenActive)) {
13
+ // it's initial mounting of the screen, do nothing here
14
+ return;
15
+ }
16
+
17
+ if (!previousIsScreenActive && isScreenActive) {
18
+ // restore focus when we go back from next screen
19
+ focusManager.setInitialFocus();
20
+ }
21
+ }, [previousIsScreenActive, isScreenActive]);
22
+ };
@@ -0,0 +1,174 @@
1
+ import * as React from "react";
2
+ import { last, pathOr } from "ramda";
3
+ import { View, StyleSheet } from "react-native";
4
+ import {
5
+ getNavigationProps,
6
+ getPathAttributes,
7
+ resolveNavigationPlugin,
8
+ } from "@applicaster/zapp-react-native-utils/navigationUtils";
9
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
10
+ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
11
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
12
+ import { useScreenBackgroundColor } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
13
+ import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
14
+
15
+ import { NavBarContainer } from "../../Layout/TV/NavBarContainer";
16
+ import { ScreenResolver } from "../../ScreenResolver";
17
+ import { useInitialFocus, useRestoreFocus } from "./hooks";
18
+
19
+ const styles = StyleSheet.create({
20
+ container: {
21
+ position: "absolute",
22
+ top: 0,
23
+ left: 0,
24
+ right: 0,
25
+ bottom: 0,
26
+ },
27
+ });
28
+
29
+ type Components = {
30
+ NavBar: React.ComponentType<any>;
31
+ Background: React.ComponentType<any>;
32
+ };
33
+
34
+ type Props = {
35
+ route: string;
36
+ Components: Components;
37
+ };
38
+
39
+ const isPlayer = (routeState): boolean => {
40
+ const pluginType = pathOr(
41
+ "unknown_plugin_type",
42
+ ["state", "screen", "plugin_type"],
43
+ routeState
44
+ );
45
+
46
+ return pluginType === "player";
47
+ };
48
+
49
+ const getActiveRiver = ({
50
+ screenId,
51
+ screenType,
52
+ routeState,
53
+ rivers,
54
+ fallbackActiveRiver,
55
+ }) => {
56
+ if (rivers[screenId]) {
57
+ if (screenType === "hooks") {
58
+ const navigations = routeState?.state?.entry?.payload?.navigations;
59
+
60
+ if (navigations) {
61
+ return routeState.state.entry.payload;
62
+ }
63
+ }
64
+
65
+ return routeState?.state?.screen;
66
+ }
67
+
68
+ // screenId is not presented into rivers, use fallback instead
69
+ return fallbackActiveRiver;
70
+ };
71
+
72
+ const getNavigations = ({
73
+ screenId,
74
+ screenType,
75
+ routeState,
76
+ rivers,
77
+ fallbackNavigations,
78
+ }) => {
79
+ if (rivers[screenId]) {
80
+ // river presents
81
+ if (screenType === "hooks") {
82
+ const navigations = routeState?.state?.entry?.payload?.navigations;
83
+
84
+ if (navigations) {
85
+ return navigations;
86
+ }
87
+ }
88
+
89
+ return routeState?.state?.screen?.navigations;
90
+ }
91
+
92
+ // screenId is not presented into rivers, use fallback instead
93
+ return fallbackNavigations;
94
+ };
95
+
96
+ export const Screen = ({ route, Components }: Props) => {
97
+ if (isNilOrEmpty(route)) {
98
+ throw Error("Required props: route is missing");
99
+ }
100
+
101
+ const navigator = useNavigation();
102
+ const { plugins = [], rivers = [] } = usePickFromState(["plugins", "rivers"]);
103
+
104
+ const pathAttributes = getPathAttributes({ pathname: route });
105
+ const routeState = navigator.getStackForPathname(route);
106
+
107
+ const { screenType, screenId } = last(pathAttributes);
108
+
109
+ const backgroundColor = useScreenBackgroundColor(screenId);
110
+
111
+ useInitialFocus();
112
+ useRestoreFocus();
113
+
114
+ const activeRiver = getActiveRiver({
115
+ screenId,
116
+ screenType,
117
+ routeState,
118
+ rivers,
119
+ fallbackActiveRiver: navigator.activeRiver,
120
+ });
121
+
122
+ const navigationProps = getNavigationProps({
123
+ navigator: {
124
+ // @ts-ignore
125
+ activeRiver,
126
+ // @ts-ignore
127
+ screenTitle: routeState.state.entry?.title || routeState.screen?.name,
128
+ },
129
+ category: "menu",
130
+ });
131
+
132
+ const navigations = getNavigations({
133
+ screenId,
134
+ screenType,
135
+ routeState,
136
+ rivers,
137
+ fallbackNavigations: navigator.activeRiver.navigations,
138
+ });
139
+
140
+ const menuPlugin = resolveNavigationPlugin({
141
+ category: "menu",
142
+ navigations,
143
+ plugins,
144
+ }) as QuickBrickPlugin;
145
+
146
+ const NavBar = (Components.NavBar ||
147
+ menuPlugin.module) as React.ComponentType<any>;
148
+
149
+ const isNavBarVisible = !isPlayer(routeState);
150
+
151
+ const screenData =
152
+ screenType === "hooks"
153
+ ? routeState?.state?.entry
154
+ : routeState?.state?.screen;
155
+
156
+ return (
157
+ <View style={[styles.container, { backgroundColor }]}>
158
+ <NavBarContainer isVisible={isNavBarVisible} onReady={noop}>
159
+ <NavBar
160
+ groupId={route}
161
+ isVisible={isNavBarVisible}
162
+ menuPlugin={menuPlugin}
163
+ navigationProps={navigationProps}
164
+ />
165
+ </NavBarContainer>
166
+ <ScreenResolver
167
+ screenType={screenType}
168
+ screenId={screenId}
169
+ screenData={screenData}
170
+ groupId={route}
171
+ />
172
+ </View>
173
+ );
174
+ };
@@ -32,12 +32,13 @@ type Props = {
32
32
  nextFocusLeft?: React.Ref<any>;
33
33
  nextFocusUp?: React.Ref<any>;
34
34
  };
35
+ groupId?: string;
35
36
  };
36
37
 
37
38
  export function ScreenResolverComponent(props: Props) {
38
39
  useScreenAnalytics(props);
39
40
 
40
- const { screenType, screenId, screenData } = props;
41
+ const { screenType, screenId, screenData, groupId } = props;
41
42
 
42
43
  const { hookPlugin } = screenData || {};
43
44
 
@@ -81,6 +82,7 @@ export function ScreenResolverComponent(props: Props) {
81
82
  item={screenData}
82
83
  mode={mode === "PIP" ? "PIP" : "FULLSCREEN"}
83
84
  isModal={false}
85
+ groupId={groupId}
84
86
  />
85
87
  );
86
88
  }
@@ -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(