@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5227180498 → 12.0.0-alpha.5347482137

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 (63) hide show
  1. package/Components/BaseFocusable/index.tsx +3 -3
  2. package/Components/Cell/Cell.tsx +5 -5
  3. package/Components/Cell/CellWithFocusable.tsx +23 -9
  4. package/Components/Cell/TvOSCellComponent.tsx +3 -7
  5. package/Components/Focusable/Focusable.tsx +6 -1
  6. package/Components/FocusableGroup/index.tsx +11 -2
  7. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  8. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  9. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  10. package/Components/Layout/TV/__tests__/index.test.tsx +19 -75
  11. package/Components/Layout/TV/index.tsx +12 -11
  12. package/Components/Layout/TV/index.web.tsx +33 -0
  13. package/Components/Layout/index.tsx +15 -11
  14. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -112
  15. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  16. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  17. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  18. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  19. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  20. package/Components/MasterCell/utils/index.ts +17 -30
  21. package/Components/Navigator/StackNavigator.tsx +54 -0
  22. package/Components/Navigator/index.ts +1 -0
  23. package/Components/PlayerContainer/PlayerContainer.tsx +8 -1
  24. package/Components/River/ComponentsMap/ComponentsMap.tsx +6 -0
  25. package/Components/River/RefreshControl.tsx +2 -2
  26. package/Components/River/TV/River.tsx +0 -23
  27. package/Components/RouteManager/index.tsx +2 -2
  28. package/Components/Screen/TV/hooks/index.ts +3 -0
  29. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  30. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  31. package/Components/Screen/TV/index.web.tsx +186 -0
  32. package/Components/Screen/__tests__/Screen.test.tsx +3 -0
  33. package/Components/Screen/index.tsx +29 -1
  34. package/Components/ScreenResolver/index.tsx +3 -1
  35. package/Components/Transitioner/Scene.tsx +24 -19
  36. package/Components/Transitioner/Transitioner.tsx +21 -28
  37. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +6 -2
  38. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  39. package/Components/VideoLive/LiveImageManager.ts +28 -1
  40. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +48 -38
  41. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +40 -33
  42. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +98 -81
  43. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +13 -5
  44. package/Components/VideoModal/ModalAnimation/utils.ts +6 -4
  45. package/Components/VideoModal/PlayerDetails.tsx +3 -3
  46. package/Components/VideoModal/PlayerWrapper.tsx +67 -60
  47. package/Components/VideoModal/VideoModal.tsx +4 -4
  48. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +7 -0
  49. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +32 -12
  50. package/Components/VideoModal/utils.ts +5 -2
  51. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  52. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  53. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  54. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  55. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  56. package/Contexts/FocusableGroupContext/index.ts +7 -0
  57. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  58. package/Contexts/ScreenDataContext/index.tsx +20 -0
  59. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  60. package/package.json +5 -5
  61. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  62. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  63. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
@@ -7,50 +7,28 @@ type BorderPosition = "inside" | "outside" | "center";
7
7
  interface Props {
8
8
  style: ImageStyle | ViewStyle;
9
9
  borderPosition: BorderPosition;
10
+ borderPaddingTop: number;
11
+ borderPaddingRight: number;
12
+ borderPaddingBottom: number;
13
+ borderPaddingLeft: number;
10
14
  children: React.ReactNode;
11
15
  }
12
16
 
13
- const MARGINS = [
14
- "marginTop",
15
- "marginBottom",
16
- "marginLeft",
17
- "marginRight",
18
- ] as const;
19
-
20
- const PADDINGS = [
21
- "paddingTop",
22
- "paddingBottom",
23
- "paddingLeft",
24
- "paddingRight",
25
- ] as const;
26
-
27
- const isPercentageValue = (value: number | string) => {
28
- if (typeof value === "string" && value.endsWith("%")) {
29
- return true;
30
- }
31
-
32
- return false;
33
- };
34
-
35
- const calculateContainerSpacing = ({
36
- spacingKey,
37
- style,
38
- additionalSpacing,
39
- }: {
40
- spacingKey: typeof MARGINS[number] | typeof PADDINGS[number];
41
- style: ImageStyle | ViewStyle;
42
- additionalSpacing: number;
43
- }) => {
44
- return {
45
- [spacingKey]:
46
- toNumberWithDefaultZero(style[spacingKey]) + additionalSpacing,
47
- };
48
- };
49
-
50
- const getContainerBorderWidth = (
51
- borderPosition: BorderPosition,
52
- borderWidth
53
- ) => {
17
+ const COLOR_TRANSPARENT = "transparent";
18
+
19
+ const styles = StyleSheet.create({
20
+ container: {
21
+ overflow: "visible",
22
+ borderWidth: 0,
23
+ borderColor: COLOR_TRANSPARENT,
24
+ },
25
+ border: {
26
+ ...StyleSheet.absoluteFillObject,
27
+ zIndex: 1,
28
+ },
29
+ });
30
+
31
+ const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
54
32
  switch (borderPosition) {
55
33
  case "inside":
56
34
  return 0;
@@ -63,104 +41,102 @@ const getContainerBorderWidth = (
63
41
  }
64
42
  };
65
43
 
66
- const getContainerPadding = (borderPosition: BorderPosition, borderWidth) => {
67
- switch (borderPosition) {
68
- case "inside":
69
- return borderWidth;
70
- case "center":
71
- return borderWidth / 2;
72
- case "outside":
73
- return 0;
74
- default:
75
- return 0;
76
- }
77
- };
78
-
79
- const InsideBorder = ({
44
+ const Border = ({
80
45
  style,
46
+ padding,
81
47
  borderPosition,
82
48
  }: {
83
49
  style: ViewStyle;
50
+ padding: ViewStyle;
84
51
  borderPosition: BorderPosition;
85
52
  }) => {
86
- if (borderPosition === "outside") {
53
+ if (!borderPosition) {
87
54
  return null;
88
55
  }
89
56
 
90
- const insideBorderWidth =
91
- borderPosition === "center" ? style.borderWidth / 2 : style.borderWidth;
57
+ const borderWidth = toNumberWithDefaultZero(style.borderWidth);
58
+ const containerPadding = getBorderPadding(borderPosition, borderWidth);
59
+
60
+ const initialBorderRadius = toNumberWithDefaultZero(style.borderRadius);
61
+ const paddingTop = toNumberWithDefaultZero(padding.paddingTop);
62
+ const paddingRight = toNumberWithDefaultZero(padding.paddingRight);
63
+ const paddingBottom = toNumberWithDefaultZero(padding.paddingBottom);
64
+ const paddingLeft = toNumberWithDefaultZero(padding.paddingLeft);
65
+
66
+ const borderRadiusStyle = initialBorderRadius
67
+ ? {
68
+ borderTopLeftRadius:
69
+ initialBorderRadius +
70
+ containerPadding +
71
+ Math.min(paddingTop, paddingLeft),
72
+ borderTopRightRadius:
73
+ initialBorderRadius +
74
+ containerPadding +
75
+ Math.min(paddingTop, paddingRight),
76
+ borderBottomLeftRadius:
77
+ initialBorderRadius +
78
+ containerPadding +
79
+ Math.min(paddingBottom, paddingLeft),
80
+ borderBottomRightRadius:
81
+ initialBorderRadius +
82
+ containerPadding +
83
+ Math.min(paddingBottom, paddingRight),
84
+ }
85
+ : {
86
+ borderTopLeftRadius: 0,
87
+ borderTopRightRadius: 0,
88
+ borderBottomLeftRadius: 0,
89
+ borderBottomRightRadius: 0,
90
+ };
92
91
 
93
92
  return (
94
93
  <View
94
+ pointerEvents="none"
95
95
  style={{
96
- ...StyleSheet.absoluteFillObject,
97
- borderWidth: insideBorderWidth,
96
+ ...styles.border,
97
+ right: -containerPadding - paddingRight,
98
+ bottom: -containerPadding - paddingBottom,
99
+ left: -containerPadding - paddingLeft,
100
+ top: -containerPadding - paddingTop,
101
+ borderWidth,
98
102
  borderColor: style.borderColor,
99
- borderRadius: style.borderRadius,
103
+ ...borderRadiusStyle,
100
104
  }}
101
105
  />
102
106
  );
103
107
  };
104
108
 
105
109
  export const BorderContainerView = (props: Props) => {
106
- const { style, children } = props;
107
- // Return border position prop after re-implementing
108
- const borderPosition = "outside";
109
-
110
- const borderWidth = toNumberWithDefaultZero(style.borderWidth);
111
- const spacing = getContainerPadding(borderPosition, borderWidth);
110
+ const {
111
+ borderPosition,
112
+ borderPaddingTop,
113
+ borderPaddingRight,
114
+ borderPaddingBottom,
115
+ borderPaddingLeft,
116
+ style,
117
+ children,
118
+ } = props;
112
119
 
113
120
  const borderStyleForMissingBorder =
114
121
  style?.borderWidth === 0 || !style?.borderColor ? { borderRadius: 0 } : {};
115
122
 
116
- const updatedMargins = MARGINS.reduce((acc, margin) => {
117
- const marginValue = calculateContainerSpacing({
118
- spacingKey: margin,
119
- style,
120
- additionalSpacing: spacing,
121
- });
122
-
123
- return { ...acc, ...marginValue };
124
- }, {});
125
-
126
- const updatedPaddings = PADDINGS.reduce((acc, padding) => {
127
- const paddingValue = calculateContainerSpacing({
128
- spacingKey: padding,
129
- style,
130
- additionalSpacing: spacing,
131
- });
132
-
133
- return { ...acc, ...paddingValue };
134
- }, {});
135
-
136
- const updatedWidth = style.width
137
- ? isPercentageValue(style.width)
138
- ? style.width
139
- : toNumberWithDefaultZero(style.width) - spacing * 2
140
- : undefined;
141
-
142
- const updatedHeight = style.height
143
- ? isPercentageValue(style.height)
144
- ? style.height
145
- : toNumberWithDefaultZero(style.height) - spacing * 2
146
- : undefined;
147
-
148
- const containerStyle = {
149
- borderWidth: getContainerBorderWidth(borderPosition, borderWidth),
150
- padding: spacing,
151
- ...updatedMargins,
152
- ...updatedPaddings,
153
- width: updatedWidth,
154
- height: updatedHeight,
155
- };
123
+ const padding =
124
+ borderPosition === "outside"
125
+ ? {
126
+ paddingTop: borderPaddingTop,
127
+ paddingRight: borderPaddingRight,
128
+ paddingBottom: borderPaddingBottom,
129
+ paddingLeft: borderPaddingLeft,
130
+ }
131
+ : {};
156
132
 
157
133
  return (
158
- <View {...props} style={[style, containerStyle]}>
134
+ <View
135
+ {...props}
136
+ style={[style, styles.container, borderStyleForMissingBorder]}
137
+ >
138
+ <Border style={style} padding={padding} borderPosition={borderPosition} />
159
139
  {children}
160
- <InsideBorder
161
- style={{ ...style, ...borderStyleForMissingBorder }}
162
- borderPosition={borderPosition}
163
- />
164
140
  </View>
165
141
  );
166
142
  };
@@ -4,6 +4,7 @@ import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerM
4
4
 
5
5
  type DynamicBadgeProps = {
6
6
  cellUUID?: string;
7
+ uri?: string;
7
8
  };
8
9
 
9
10
  const DynamicBadgeComponent = (props: DynamicBadgeProps) => {
@@ -12,11 +13,12 @@ const DynamicBadgeComponent = (props: DynamicBadgeProps) => {
12
13
  // TODO: Initial value should depends on player state (playing or not)
13
14
  const [showImage, setShowImage] = React.useState(true);
14
15
  const player = usePlayer(playerId);
16
+ const uri = props?.uri;
15
17
 
16
18
  React.useEffect(() => {
17
19
  if (playerId && player) {
18
20
  return player.addListener({
19
- id: "dynamic-badges",
21
+ id: `dynamic-badges-${uri}`,
20
22
  listener: {
21
23
  onPlayerClose: () => {
22
24
  setShowImage(true);
@@ -35,7 +37,7 @@ const DynamicBadgeComponent = (props: DynamicBadgeProps) => {
35
37
  } else {
36
38
  setShowImage(true);
37
39
  }
38
- }, [player]);
40
+ }, [player, uri]);
39
41
 
40
42
  if (!showImage) {
41
43
  return null;
@@ -4,6 +4,7 @@ import { Focusable } from "@applicaster/zapp-react-native-ui-components/Componen
4
4
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
5
  import * as R from "ramda";
6
6
  import { getXray } from "@applicaster/zapp-react-native-utils/logger";
7
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
7
8
 
8
9
  const { Logger } = getXray();
9
10
 
@@ -35,7 +36,9 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
35
36
 
36
37
  const focusableId = getFocusableId(prefixId, suffixId);
37
38
 
38
- const focused = focusableId === focusedButtonId;
39
+ const focused =
40
+ focusableId === focusedButtonId ||
41
+ toBooleanWithDefaultFalse(otherProps.focused);
39
42
 
40
43
  const additionalStyles = focused ? focusedStyles : normalStyles;
41
44
 
@@ -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
  )}
@@ -7,6 +7,10 @@ import type {
7
7
  ContainerProps,
8
8
  ContainerChildren,
9
9
  } from "./types";
10
+ import {
11
+ useIsRTL,
12
+ applyRTLStylesIfNeeded,
13
+ } from "@applicaster/zapp-react-native-utils/localizationUtils";
10
14
 
11
15
  const HorizontalSeparator = ({ width }: HorizontalSeparatorProps) => (
12
16
  <View style={{ width }} />
@@ -17,6 +21,7 @@ export function ButtonContainerView({
17
21
  children,
18
22
  ...otherProps
19
23
  }: ContainerProps) {
24
+ const isRTL = useIsRTL();
20
25
  const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
21
26
 
22
27
  const filteredChildren = useFilterChildren<ContainerChildren>(children);
@@ -26,10 +31,13 @@ export function ButtonContainerView({
26
31
  }
27
32
 
28
33
  return (
29
- <View style={style}>
34
+ <View style={applyRTLStylesIfNeeded(style, isRTL)}>
30
35
  {insertBetween(
31
36
  (index) => (
32
- <HorizontalSeparator key={index} width={horizontalGutter} />
37
+ <HorizontalSeparator
38
+ key={`separator_${index}`}
39
+ width={horizontalGutter}
40
+ />
33
41
  ),
34
42
  filteredChildren
35
43
  )}
@@ -9,4 +9,13 @@ describe("insertBetween", () => {
9
9
 
10
10
  expect(result).toEqual([1, "|", 2, "|", 3]);
11
11
  });
12
+
13
+ it("insertBetween", () => {
14
+ const separator = (index) => `separator:${index}`;
15
+ const items = [1, 2, 3];
16
+
17
+ const result = insertBetween(separator, items);
18
+
19
+ expect(result).toEqual([1, "separator:0", 2, "separator:1", 3]);
20
+ });
12
21
  });
@@ -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,7 @@ 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";
12
14
 
13
15
  const hasElementSpecificViewType = (viewType) => (element) => {
14
16
  if (R.isNil(element)) {
@@ -47,44 +49,29 @@ export const hasElementsSpecificViewType = (viewType) => (elements) => {
47
49
  return R.any(hasElementSpecificViewType(viewType))(elements);
48
50
  };
49
51
 
50
- const isColorOrEmpty = (value) =>
51
- value === null ||
52
- value === undefined ||
53
- value === "" ||
54
- value.startsWith("#") ||
55
- value.startsWith("rgba") ||
56
- value === "transparent";
57
-
58
52
  function resolveColorForProp(entry, style, colorProp) {
59
- const colorValue = style[colorProp];
60
-
61
- if (isColorOrEmpty(colorValue)) {
62
- return style;
53
+ const colorFromProp = style[colorProp];
54
+ const nestedEntryValue = R.path(colorFromProp.split("."), entry);
55
+
56
+ if (
57
+ nestedEntryValue === undefined &&
58
+ !validateColor(colorFromProp.replace(".00", "")) // https://github.com/dreamyguy/validate-color/issues/44
59
+ ) {
60
+ logWarning(colorFromProp, style, entry, nestedEntryValue);
63
61
  }
64
62
 
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
- }
63
+ const colorValue = getColorFromData({
64
+ data: entry,
65
+ valueFromLayout: colorFromProp,
66
+ });
77
67
 
78
- if (nestedEntryValue === undefined) {
79
- logWarning(colorValue, style, entry, nestedEntryValue);
68
+ if (!colorValue) {
69
+ logWarning(colorProp, style, entry, colorValue);
80
70
 
81
71
  return style;
82
72
  }
83
73
 
84
- // After successful traversal, set the value.
85
- style[colorProp] = nestedEntryValue;
86
-
87
- return style;
74
+ return { ...style, [colorProp]: colorValue };
88
75
  }
89
76
 
90
77
  export function resolveColor(entry, style) {
@@ -0,0 +1,54 @@
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
+
13
+ type Components = {
14
+ NavBar: React.ComponentType<any>;
15
+ Background: React.ComponentType<any>;
16
+ };
17
+
18
+ type Props = {
19
+ Components: Components;
20
+ };
21
+
22
+ export const StackNavigator = ({ Components }: Props) => {
23
+ const { mainStack } = useNavigation();
24
+
25
+ const renderScreen = ({ route, state }, index) => {
26
+ const pathAttributes = getPathAttributes({ pathname: route });
27
+
28
+ if (isEmpty(pathAttributes)) {
29
+ return null;
30
+ }
31
+
32
+ // HACK: Adding index to route to avoid conflicts in the navigator.
33
+ // Startup hooks are all using PUSH navigation action to navigate, but their routes are always like root routes.
34
+ // This means some hooks can have the same routes, e.g., ["hooks/context-setter", "/hooks/login-plug", "hooks/context-setter"].
35
+ const routeId = `${route}--${index}`;
36
+
37
+ return (
38
+ <FocusableGroup
39
+ id={route}
40
+ preferredFocus={isLast(index, mainStack.length)}
41
+ excludeFromFocusSearching
42
+ key={routeId}
43
+ >
44
+ <ScreenDataContext.Provider value={state}>
45
+ <PathnameContext.Provider value={route}>
46
+ <Screen route={route} Components={Components} />
47
+ </PathnameContext.Provider>
48
+ </ScreenDataContext.Provider>
49
+ </FocusableGroup>
50
+ );
51
+ };
52
+
53
+ return <>{mainStack.map(renderScreen)}</>;
54
+ };
@@ -0,0 +1 @@
1
+ export { StackNavigator } from "./StackNavigator";
@@ -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,8 +251,12 @@ const PlayerContainerComponent = (props: Props) => {
249
251
  return;
250
252
  }
251
253
 
252
- navigator.goBack();
253
254
  showNavBar(true);
255
+
256
+ // Hack to close player screen on mobile only (tv handles that in InteractionManager)
257
+ if (!isTV()) {
258
+ navigator.goBack();
259
+ }
254
260
  }, [isModal, navigator.goBack, showNavBar]);
255
261
 
256
262
  const playEntry = (entry) => navigator.replaceTop(entry, { mode });
@@ -629,6 +635,7 @@ const PlayerContainerComponent = (props: Props) => {
629
635
  style={styles.focusableGroup}
630
636
  preferredFocus
631
637
  shouldUsePreferredFocus
638
+ groupId={groupId}
632
639
  >
633
640
  {/* Video player and components */}
634
641
  <View
@@ -271,6 +271,12 @@ function ComponentsMapComponent(props: Props) {
271
271
 
272
272
  if (y >= 0 && y + height <= contentHeight) {
273
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
+ }
274
280
  }
275
281
  }, []);
276
282
 
@@ -134,10 +134,10 @@ export const useGetProgressViewOffset = () => {
134
134
  switch (contentPosition) {
135
135
  case "on_bottom_status_bar":
136
136
  // navbar height
137
- return offset + height - top;
137
+ return offset + height;
138
138
  case "on_top_screen":
139
139
  // navbar height + status bar height
140
- return offset + height;
140
+ return offset + height + top;
141
141
  default:
142
142
  return offset;
143
143
  }
@@ -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";