@applicaster/zapp-react-native-ui-components 12.0.0-alpha.4837064762 → 12.0.0-alpha.4885205607

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 (84) hide show
  1. package/Components/BaseFocusable/index.tsx +3 -3
  2. package/Components/Cell/Cell.tsx +30 -13
  3. package/Components/Cell/CellWithFocusable.tsx +6 -4
  4. package/Components/Cell/TvOSCellComponent.tsx +3 -7
  5. package/Components/Focusable/Focusable.tsx +6 -1
  6. package/Components/FocusableGroup/index.tsx +10 -2
  7. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  8. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  9. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  10. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  11. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  12. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  13. package/Components/Layout/TV/__tests__/index.test.tsx +20 -75
  14. package/Components/Layout/TV/index.tsx +29 -181
  15. package/Components/Layout/TV/index.web.tsx +33 -0
  16. package/Components/Layout/index.tsx +15 -11
  17. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +4 -1
  18. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -112
  19. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  20. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  21. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  22. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +20 -0
  23. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  24. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  25. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  26. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  27. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  28. package/Components/MasterCell/dataAdapter.ts +21 -7
  29. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  30. package/Components/MasterCell/index.tsx +5 -3
  31. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  32. package/Components/MasterCell/utils/index.ts +32 -29
  33. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  34. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  35. package/Components/Navigator/StackNavigator.tsx +57 -0
  36. package/Components/Navigator/index.ts +1 -0
  37. package/Components/PlayerContainer/PlayerContainer.tsx +3 -0
  38. package/Components/River/ComponentsMap/ComponentsMap.tsx +24 -17
  39. package/Components/River/RefreshControl.tsx +55 -1
  40. package/Components/River/TV/River.tsx +0 -23
  41. package/Components/RouteManager/index.tsx +2 -2
  42. package/Components/Screen/TV/hooks/index.ts +3 -0
  43. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  44. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  45. package/Components/Screen/TV/index.web.tsx +59 -45
  46. package/Components/Screen/__tests__/Screen.test.tsx +3 -0
  47. package/Components/Screen/index.tsx +55 -58
  48. package/Components/Screen/navigationHandler.ts +5 -0
  49. package/Components/ScreenResolver/index.tsx +6 -11
  50. package/Components/Transitioner/Scene.tsx +32 -26
  51. package/Components/Transitioner/Transitioner.tsx +38 -43
  52. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +12 -4
  53. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  54. package/Components/VideoLive/LiveImageManager.ts +28 -1
  55. package/Components/VideoLive/PlayerLiveImageComponent.tsx +52 -24
  56. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +26 -24
  57. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +2 -14
  58. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +56 -43
  59. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +42 -32
  60. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +104 -86
  61. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +44 -5
  62. package/Components/VideoModal/ModalAnimation/utils.ts +7 -4
  63. package/Components/VideoModal/PlayerDetails.tsx +3 -3
  64. package/Components/VideoModal/PlayerWrapper.tsx +67 -60
  65. package/Components/VideoModal/VideoModal.tsx +17 -17
  66. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +7 -0
  67. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +32 -12
  68. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  69. package/Components/VideoModal/utils.ts +5 -2
  70. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  71. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  72. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  73. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  74. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  75. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  76. package/Contexts/FocusableGroupContext/index.ts +7 -0
  77. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  78. package/Contexts/ScreenContext/index.tsx +2 -2
  79. package/Contexts/ScreenDataContext/index.tsx +20 -0
  80. package/events/index.ts +1 -0
  81. package/package.json +5 -5
  82. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  83. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  84. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect, useMemo } from "react";
2
2
  import * as R from "ramda";
3
+ import validateColor from "validate-color";
3
4
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
4
5
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
6
 
@@ -9,6 +10,13 @@ import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerM
9
10
  import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
10
11
  import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
11
12
  import { getCellState } from "../../Cell/utils";
13
+ import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
14
+ import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
15
+
16
+ const log = createLogger({
17
+ category: "Debug",
18
+ subsystem: "MasterCellUtils",
19
+ });
12
20
 
13
21
  const hasElementSpecificViewType = (viewType) => (element) => {
14
22
  if (R.isNil(element)) {
@@ -47,44 +55,37 @@ export const hasElementsSpecificViewType = (viewType) => (elements) => {
47
55
  return R.any(hasElementSpecificViewType(viewType))(elements);
48
56
  };
49
57
 
50
- const isColorOrEmpty = (value) =>
51
- value === null ||
52
- value === undefined ||
53
- value === "" ||
54
- value.startsWith("#") ||
55
- value.startsWith("rgba") ||
56
- value === "transparent";
57
-
58
58
  function resolveColorForProp(entry, style, colorProp) {
59
- const colorValue = style[colorProp];
59
+ const colorFromProp = style[colorProp];
60
+ const nestedEntryValue = R.path(colorFromProp.split("."), entry);
61
+
62
+ log.log_error("resolvecolor resolveColorforProp", {
63
+ colorFromProp,
64
+ nestedEntryValue,
65
+ entry,
66
+ style,
67
+ colorProp,
68
+ });
60
69
 
61
- if (isColorOrEmpty(colorValue)) {
62
- return style;
70
+ if (
71
+ nestedEntryValue === undefined &&
72
+ !validateColor(colorFromProp.replace(".00", "")) // https://github.com/dreamyguy/validate-color/issues/44
73
+ ) {
74
+ logWarning(colorFromProp, style, entry, nestedEntryValue);
63
75
  }
64
76
 
65
- const pathParts = colorValue.split(".");
66
- let nestedEntryValue = entry;
67
-
68
- for (const part of pathParts) {
69
- if (nestedEntryValue === null || typeof nestedEntryValue !== "object") {
70
- logWarning(colorValue, style, entry, nestedEntryValue);
71
-
72
- return style;
73
- }
74
-
75
- nestedEntryValue = nestedEntryValue[part];
76
- }
77
+ const colorValue = getColorFromData({
78
+ data: entry,
79
+ valueFromLayout: colorFromProp,
80
+ });
77
81
 
78
- if (nestedEntryValue === undefined) {
79
- logWarning(colorValue, style, entry, nestedEntryValue);
82
+ if (!colorValue) {
83
+ logWarning(colorProp, style, entry, colorValue);
80
84
 
81
85
  return style;
82
86
  }
83
87
 
84
- // After successful traversal, set the value.
85
- style[colorProp] = nestedEntryValue;
86
-
87
- return style;
88
+ return { ...style, [colorProp]: colorValue };
88
89
  }
89
90
 
90
91
  export function resolveColor(entry, style) {
@@ -96,6 +97,8 @@ export function resolveColor(entry, style) {
96
97
  const styleKeys = Object.keys(style);
97
98
  const colorKeys = styleKeys.filter((key) => /color/i.test(key));
98
99
 
100
+ log.log_error("resolvecolor resolveColor", { colorKeys, entry, style });
101
+
99
102
  return colorKeys.reduce((acc, value) => {
100
103
  return { ...style, ...resolveColorForProp(entry, acc, value) };
101
104
  }, style);
@@ -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);
@@ -0,0 +1,57 @@
1
+ import * as React from "react";
2
+ import { isEmpty } from "ramda";
3
+
4
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
5
+ import { getPathAttributes } from "@applicaster/zapp-react-native-utils/navigationUtils";
6
+ import { FocusableGroup } from "@applicaster/zapp-react-native-ui-components/Components/FocusableGroup";
7
+ import { ScreenDataContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenDataContext";
8
+ import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
9
+
10
+ import { Screen } from "../Screen/TV/index.web";
11
+ import { isLast } from "@applicaster/zapp-react-native-utils/arrayUtils";
12
+ import { ScreenContextProvider } from "../../Contexts/ScreenContext";
13
+
14
+ type Components = {
15
+ NavBar: React.ComponentType<any>;
16
+ Background: React.ComponentType<any>;
17
+ };
18
+
19
+ type Props = {
20
+ Components: Components;
21
+ };
22
+
23
+ export const StackNavigator = ({ Components }: Props) => {
24
+ const { mainStack } = useNavigation();
25
+
26
+ const renderScreen = ({ route, state }, index) => {
27
+ const pathAttributes = getPathAttributes({ pathname: route });
28
+
29
+ if (isEmpty(pathAttributes)) {
30
+ return null;
31
+ }
32
+
33
+ // HACK: Adding index to route to avoid conflicts in the navigator.
34
+ // Startup hooks are all using PUSH navigation action to navigate, but their routes are always like root routes.
35
+ // This means some hooks can have the same routes, e.g., ["hooks/context-setter", "/hooks/login-plug", "hooks/context-setter"].
36
+ const routeId = `${route}--${index}`;
37
+
38
+ return (
39
+ <FocusableGroup
40
+ id={route}
41
+ preferredFocus={isLast(index, mainStack.length)}
42
+ excludeFromFocusSearching
43
+ key={routeId}
44
+ >
45
+ <ScreenDataContext.Provider value={state}>
46
+ <PathnameContext.Provider value={route}>
47
+ <ScreenContextProvider pathname={route}>
48
+ <Screen route={route} Components={Components} />
49
+ </ScreenContextProvider>
50
+ </PathnameContext.Provider>
51
+ </ScreenDataContext.Provider>
52
+ </FocusableGroup>
53
+ );
54
+ };
55
+
56
+ return <>{mainStack.map(renderScreen)}</>;
57
+ };
@@ -0,0 +1 @@
1
+ export { StackNavigator } from "./StackNavigator";
@@ -124,6 +124,7 @@ const nativeStyles = {
124
124
  playerScreen: {
125
125
  flex: 1,
126
126
  backgroundColor: "black",
127
+ overflow: "hidden",
127
128
  },
128
129
  playerWrapper: {
129
130
  flex: 1,
@@ -252,6 +253,7 @@ const PlayerContainerComponent = (props: Props) => {
252
253
  }
253
254
 
254
255
  showNavBar(true);
256
+ navigator.goBack();
255
257
  }, [isModal, navigator.goBack, showNavBar]);
256
258
 
257
259
  const playEntry = (entry) => navigator.replaceTop(entry, { mode });
@@ -673,6 +675,7 @@ const PlayerContainerComponent = (props: Props) => {
673
675
  setNextVideoPreloadThresholdPercentage={
674
676
  setNextVideoPreloadThresholdPercentage
675
677
  }
678
+ startComponentsAnimation={startComponentsAnimation}
676
679
  >
677
680
  {renderApplePlayer(applePlayerProps)}
678
681
  </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,26 @@ 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
+ } else {
275
+ if (y <= 0) {
276
+ scrollY.setValue(0);
277
+ } else if (y + height > contentHeight) {
278
+ scrollY.setValue(contentHeight - height);
279
+ }
280
+ }
281
+ }, []);
282
+
263
283
  if (shouldShowLoadingError) {
264
284
  return <RiverError />;
265
285
  }
@@ -298,20 +318,7 @@ function ComponentsMapComponent(props: Props) {
298
318
  ListFooterComponent={renderFooter}
299
319
  refreshControl={refreshControl}
300
320
  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
- )}
321
+ onScroll={onScroll}
315
322
  onMomentumScrollEnd={_onMomentumScrollEnd}
316
323
  onScrollEndDrag={_onScrollEndDrag}
317
324
  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;
138
+ case "on_top_screen":
139
+ // navbar height + status bar height
140
+ return offset + height + top;
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!`,
@@ -14,7 +14,7 @@ type Props = { pathname: string; screenData: Record<string, any> };
14
14
  * @param {Props}: see props above
15
15
  */
16
16
  export function RouteManager(props: Props) {
17
- const { pathname, screenData: _screenData } = useRoute();
17
+ const { pathname, screenData } = useRoute();
18
18
  const pathAttributes = getPathAttributes({ pathname });
19
19
 
20
20
  const { screenType, screenId } = R.last(pathAttributes);
@@ -25,7 +25,7 @@ export function RouteManager(props: Props) {
25
25
  screenType={screenType}
26
26
  screenId={screenId}
27
27
  {...props}
28
- screenData={props.screenData}
28
+ screenData={screenData}
29
29
  />
30
30
  </TestId>
31
31
  );
@@ -0,0 +1,3 @@
1
+ export { useInitialFocus } from "./useInitialFocus";
2
+
3
+ export { useRestoreFocus } from "./useRestoreFocus";
@@ -0,0 +1,70 @@
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 {
9
+ setFocusOnContent,
10
+ setFocusOnMenu,
11
+ } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux";
12
+
13
+ type Return =
14
+ | {
15
+ onContent: true;
16
+ onMenu: false;
17
+ }
18
+ | {
19
+ onContent: false;
20
+ onMenu: true;
21
+ };
22
+
23
+ const getInitialFocus = (focusOnContent, isNavBarVisible): Return => {
24
+ if (focusOnContent) {
25
+ return {
26
+ onContent: true,
27
+ onMenu: false,
28
+ };
29
+ }
30
+
31
+ if (!focusOnContent && isNavBarVisible) {
32
+ // put focus on navbar
33
+ return {
34
+ onContent: false,
35
+ onMenu: true,
36
+ };
37
+ }
38
+
39
+ // we don't have navbar, put focus on content anyway
40
+ return {
41
+ onContent: true,
42
+ onMenu: false,
43
+ };
44
+ };
45
+
46
+ export const useInitialFocus = (): void => {
47
+ const navigationPluginData = useNavigationPluginData();
48
+
49
+ const focusOnContent = toBooleanWithDefaultFalse(
50
+ navigationPluginData?.rules?.focus_on_content
51
+ );
52
+
53
+ const isNavBarVisible = useIsNavBarVisible();
54
+
55
+ const { pathname: currentRoute } = useRoute();
56
+
57
+ React.useEffect(() => {
58
+ const initialFocus = getInitialFocus(focusOnContent, isNavBarVisible);
59
+
60
+ if (initialFocus.onContent) {
61
+ setFocusOnContent(currentRoute);
62
+
63
+ return;
64
+ }
65
+
66
+ if (initialFocus.onMenu) {
67
+ setFocusOnMenu(currentRoute);
68
+ }
69
+ }, []);
70
+ };
@@ -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
+ };