@applicaster/zapp-react-native-ui-components 12.0.0-alpha.3977160850 → 12.0.0-alpha.4201443043

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 (90) hide show
  1. package/Components/BaseFocusable/index.tsx +1 -1
  2. package/Components/Cell/Cell.tsx +7 -7
  3. package/Components/Cell/CellWithFocusable.tsx +38 -19
  4. package/Components/Cell/TvOSCellComponent.tsx +2 -2
  5. package/Components/ErrorScreen/index.tsx +1 -1
  6. package/Components/FocusableCell/index.tsx +7 -7
  7. package/Components/FocusableGroup/index.tsx +1 -0
  8. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  9. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  10. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  11. package/Components/HookRenderer/index.tsx +1 -3
  12. package/Components/Image/Image.android.tsx +3 -25
  13. package/Components/Image/Image.tsx +18 -2
  14. package/Components/Image/utils/index.ts +39 -0
  15. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  16. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -10
  17. package/Components/Layout/TV/__tests__/index.test.tsx +18 -86
  18. package/Components/Layout/TV/index.tsx +9 -105
  19. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  20. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  21. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  22. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  23. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  24. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  25. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
  26. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  27. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  28. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  29. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  30. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  31. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  32. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +4 -1
  33. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  34. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  35. package/Components/MasterCell/utils/index.ts +18 -7
  36. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  37. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  38. package/Components/Navigator/StackNavigator.tsx +48 -0
  39. package/Components/Navigator/index.ts +1 -0
  40. package/Components/PlayerContainer/PlayerContainer.tsx +64 -31
  41. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  42. package/Components/PlayerImageBackground/index.tsx +34 -8
  43. package/Components/River/ComponentsMap/ComponentsMap.tsx +92 -22
  44. package/Components/River/RefreshControl.tsx +55 -1
  45. package/Components/River/TV/River.tsx +0 -23
  46. package/Components/River/__tests__/componentsMap.test.js +7 -0
  47. package/Components/Screen/TV/hooks/index.ts +3 -0
  48. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  49. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  50. package/Components/Screen/TV/index.web.tsx +179 -0
  51. package/Components/Screen/__tests__/Screen.test.tsx +5 -1
  52. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +14 -0
  53. package/Components/Screen/index.tsx +48 -28
  54. package/Components/ScreenResolver/index.tsx +3 -1
  55. package/Components/Tabs/TV/styles.ts +1 -1
  56. package/Components/Transitioner/Scene.tsx +24 -19
  57. package/Components/Transitioner/Transitioner.tsx +9 -2
  58. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +6 -2
  59. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  60. package/Components/VideoLive/LiveImageManager.ts +18 -1
  61. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -28
  62. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  63. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +18 -28
  64. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
  65. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +342 -0
  66. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +151 -0
  67. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +438 -0
  68. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +154 -0
  69. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  70. package/Components/VideoModal/ModalAnimation/utils.ts +299 -0
  71. package/Components/VideoModal/PlayerWrapper.tsx +78 -48
  72. package/Components/VideoModal/VideoModal.tsx +25 -42
  73. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -1
  74. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +246 -166
  75. package/Components/VideoModal/utils.ts +3 -3
  76. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  77. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  78. package/Components/Viewport/utils.js +1 -1
  79. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  80. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  81. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  82. package/Contexts/ScreenContext/index.tsx +94 -25
  83. package/Contexts/ScreenDataContext/index.ts +3 -0
  84. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  85. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  86. package/events/index.ts +1 -0
  87. package/package.json +5 -5
  88. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  89. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  90. /package/Contexts/{index.js → index.ts} +0 -0
@@ -6,6 +6,12 @@ import configureStore from "redux-mock-store";
6
6
 
7
7
  import * as themeUtils from "@applicaster/zapp-react-native-utils/theme";
8
8
 
9
+ // silences the errors related to React-native-gesture-handler in tests
10
+ jest.mock(
11
+ "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation",
12
+ () => () => null
13
+ );
14
+
9
15
  jest.mock("react-native-safe-area-context", () => ({
10
16
  useSafeAreaInsets: () => ({ top: 50 }),
11
17
  }));
@@ -83,6 +89,7 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
83
89
  useNavigation: jest.fn(() => ({ currentRoute: "/river/A1234" })),
84
90
  useRoute: jest.fn(() => ({ pathname: "/river/A1234", screenData })),
85
91
  useProfilerLogging: jest.fn(),
92
+ useIsScreenActive: jest.fn().mockReturnValue(true),
86
93
  }));
87
94
 
88
95
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
@@ -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
+ };
@@ -0,0 +1,179 @@
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 getPluginType = pathOr("unknown_plugin_type", [
40
+ "state",
41
+ "screen",
42
+ "plugin_type",
43
+ ]);
44
+
45
+ const isPlayerPlugin = (routeState): boolean =>
46
+ getPluginType(routeState) === "player";
47
+
48
+ const isLoginPlugin = (routeState): boolean =>
49
+ getPluginType(routeState) === "login";
50
+
51
+ const getActiveRiver = ({
52
+ screenId,
53
+ screenType,
54
+ routeState,
55
+ rivers,
56
+ fallbackActiveRiver,
57
+ }) => {
58
+ if (rivers[screenId]) {
59
+ if (screenType === "hooks") {
60
+ const navigations = routeState?.state?.entry?.payload?.navigations;
61
+
62
+ if (navigations) {
63
+ return routeState.state.entry.payload;
64
+ }
65
+ }
66
+
67
+ return routeState?.state?.screen;
68
+ }
69
+
70
+ // screenId is not presented into rivers, use fallback instead
71
+ return fallbackActiveRiver;
72
+ };
73
+
74
+ const getNavigations = ({
75
+ screenId,
76
+ screenType,
77
+ routeState,
78
+ rivers,
79
+ fallbackNavigations,
80
+ }) => {
81
+ if (rivers[screenId]) {
82
+ // river presents
83
+ if (screenType === "hooks") {
84
+ const navigations = routeState?.state?.entry?.payload?.navigations;
85
+
86
+ if (navigations) {
87
+ return navigations;
88
+ }
89
+ }
90
+
91
+ return routeState?.state?.screen?.navigations;
92
+ }
93
+
94
+ // screenId is not presented into rivers, use fallback instead
95
+ return fallbackNavigations;
96
+ };
97
+
98
+ export const Screen = ({ route, Components }: Props) => {
99
+ if (isNilOrEmpty(route)) {
100
+ throw Error("Required props: route is missing");
101
+ }
102
+
103
+ const navigator = useNavigation();
104
+ const { plugins = [], rivers = [] } = usePickFromState(["plugins", "rivers"]);
105
+
106
+ const pathAttributes = getPathAttributes({ pathname: route });
107
+ const routeState = navigator.getStackForPathname(route);
108
+
109
+ const { screenType, screenId } = last(pathAttributes);
110
+
111
+ const backgroundColor = useScreenBackgroundColor(screenId);
112
+
113
+ useInitialFocus();
114
+ useRestoreFocus();
115
+
116
+ const activeRiver = getActiveRiver({
117
+ screenId,
118
+ screenType,
119
+ routeState,
120
+ rivers,
121
+ fallbackActiveRiver: navigator.activeRiver,
122
+ });
123
+
124
+ const navigationProps = getNavigationProps({
125
+ navigator: {
126
+ // @ts-ignore
127
+ activeRiver,
128
+ // @ts-ignore
129
+ screenTitle: routeState.state.entry?.title || routeState.screen?.name,
130
+ },
131
+ category: "menu",
132
+ });
133
+
134
+ const navigations = getNavigations({
135
+ screenId,
136
+ screenType,
137
+ routeState,
138
+ rivers,
139
+ fallbackNavigations: navigator.activeRiver.navigations,
140
+ });
141
+
142
+ const menuPlugin = resolveNavigationPlugin({
143
+ category: "menu",
144
+ navigations,
145
+ plugins,
146
+ }) as QuickBrickPlugin;
147
+
148
+ const NavBar = (Components.NavBar ||
149
+ menuPlugin.module) as React.ComponentType<any>;
150
+
151
+ const isPlayer = isPlayerPlugin(routeState);
152
+ const isLogin = isLoginPlugin(routeState);
153
+
154
+ const isNavBarVisible = !isPlayer && !isLogin;
155
+
156
+ const screenData =
157
+ screenType === "hooks" || isPlayer
158
+ ? routeState?.state?.entry
159
+ : routeState?.state?.screen;
160
+
161
+ return (
162
+ <View style={[styles.container, { backgroundColor }]}>
163
+ <NavBarContainer isVisible={isNavBarVisible} onReady={noop}>
164
+ <NavBar
165
+ groupId={route}
166
+ isVisible={isNavBarVisible}
167
+ menuPlugin={menuPlugin}
168
+ navigationProps={navigationProps}
169
+ />
170
+ </NavBarContainer>
171
+ <ScreenResolver
172
+ screenType={screenType}
173
+ screenId={screenId}
174
+ screenData={screenData}
175
+ groupId={route}
176
+ />
177
+ </View>
178
+ );
179
+ };
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { View } from "react-native";
3
- import { create, act } from "react-test-renderer";
3
+ import { act, create } from "react-test-renderer";
4
4
 
5
5
  const Mocked_RouteManager = jest.fn((props) => (
6
6
  <View testID="routeManager" {...props} />
@@ -69,12 +69,16 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
69
69
  isNavBarVisible: mockIsNavBarVisible,
70
70
  useRoute: jest.fn(() => ({
71
71
  pathname: "/river/testId",
72
+ screenData: { id: "testId" },
72
73
  })),
73
74
  useNavigation: jest.fn(() => ({
74
75
  canGoBack: () => false,
75
76
  currentRoute: "/river/testId",
77
+ activeRiver: { id: "testId" },
78
+ screenData: { id: "testId" },
76
79
  data: { screen: { id: "testId" } },
77
80
  })),
81
+ useIsScreenActive: jest.fn().mockReturnValue(true),
78
82
  }));
79
83
 
80
84
  jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
@@ -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=""
@@ -19,6 +21,11 @@ exports[`<Screen Component /> when the navbar should be hidden renders correctly
19
21
  <View>
20
22
  <View
21
23
  pathname="/river/testId"
24
+ screenData={
25
+ Object {
26
+ "id": "testId",
27
+ }
28
+ }
22
29
  testID="routeManager"
23
30
  />
24
31
  </View>
@@ -36,7 +43,9 @@ exports[`<Screen Component /> when the navbar should show renders correctly 1`]
36
43
  }
37
44
  >
38
45
  <View
46
+ hasMenu={false}
39
47
  id="/river/testId"
48
+ pathname="/river/testId"
40
49
  selected="testId"
41
50
  testID="navBar"
42
51
  title=""
@@ -44,6 +53,11 @@ exports[`<Screen Component /> when the navbar should show renders correctly 1`]
44
53
  <View>
45
54
  <View
46
55
  pathname="/river/testId"
56
+ screenData={
57
+ Object {
58
+ "id": "testId",
59
+ }
60
+ }
47
61
  testID="routeManager"
48
62
  />
49
63
  </View>
@@ -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,
@@ -14,6 +17,7 @@ import {
14
17
  useCurrentScreenData,
15
18
  useNavbarState,
16
19
  useScreenData,
20
+ useNavigation,
17
21
  } from "@applicaster/zapp-react-native-utils/reactHooks";
18
22
  import { getNavigationPluginModule } from "@applicaster/zapp-react-native-app/App/Layout/layoutHelpers";
19
23
 
@@ -22,6 +26,17 @@ import { RouteManager } from "../RouteManager";
22
26
  import { useScreenConfiguration } from "../River/useScreenConfiguration";
23
27
  import { useScreenOrientationHandler } from "./orientationHandler";
24
28
 
29
+ const getScreenId = (
30
+ screenData: LegacyNavigationScreenData,
31
+ fallbackRiver: ZappRiver
32
+ ) => {
33
+ return screenData?.targetScreen
34
+ ? screenData?.targetScreen?.id ?? fallbackRiver.id
35
+ : "screen_id" in screenData
36
+ ? screenData.screen_id
37
+ : fallbackRiver.id;
38
+ };
39
+
25
40
  function colorIsNotTransparent(color) {
26
41
  const layers = R.compose(
27
42
  R.map(R.trim),
@@ -40,14 +55,13 @@ function isValidColor(string) {
40
55
  }
41
56
 
42
57
  type Props = {
43
- pathname: string;
58
+ pathname: unknown;
44
59
  screenData: unknown;
45
- setToggleMenu: () => void;
46
- setCloseMenu: () => void;
47
60
  };
48
61
 
49
- export function Screen({ setToggleMenu, setCloseMenu }: Props) {
62
+ export function Screen(_props: Props) {
50
63
  const theme = useTheme<BaseThemePropertiesMobile>();
64
+ const navigation = useNavigation();
51
65
  const { plugins } = usePickFromState(["plugins"]);
52
66
 
53
67
  // Gets the data for the current screen configuration
@@ -59,28 +73,38 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
59
73
  fullDimensions: true,
60
74
  });
61
75
 
62
- const routeData = useRoute();
76
+ const { screenData, pathname } = useRoute();
63
77
 
64
78
  const currentRiver = useScreenData(
65
- (routeData.screenData as ZappRiver)?.targetScreen
66
- ? (routeData.screenData as ZappRiver)?.targetScreen.id
67
- : (routeData.screenData as ZappRiver)?.screen_id
79
+ getScreenId(screenData, navigation.activeRiver)
68
80
  );
69
81
 
70
82
  const { title } = useNavbarState();
71
83
 
72
- const navBarProps = React.useMemo(
73
- () => ({
74
- ...getNavigationPropsV2({
75
- currentRiver,
76
- title,
77
- category: "nav_bar",
78
- }),
79
- setToggleMenu,
80
- setCloseMenu,
81
- }),
82
- [currentRiver, setCloseMenu, setToggleMenu]
83
- );
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]);
84
108
 
85
109
  const NavBar = React.useMemo(
86
110
  () =>
@@ -88,12 +112,10 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
88
112
  "nav_bar",
89
113
  currentRiver,
90
114
  plugins
91
- ) as React.ComponentType<MobileNavBarPluginProps>,
115
+ ) as React.ComponentType<MobileNavBarPluginProps & { hasMenu: boolean }>,
92
116
  []
93
117
  );
94
118
 
95
- const { screenData, pathname } = routeData;
96
-
97
119
  const offlinePlugin = getComponentModule({
98
120
  plugins,
99
121
  components: {},
@@ -124,11 +146,9 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
124
146
  return (
125
147
  <ScreenContextProvider pathname={pathname}>
126
148
  <View style={style}>
127
- <NavBar
128
- {...{ ...(navBarProps as MobileNavBarPluginProps), id: pathname }}
129
- />
149
+ {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
130
150
 
131
- <OfflineFallbackScreen screenData={screenData}>
151
+ <OfflineFallbackScreen screenData={targetScreenData}>
132
152
  {/* @TODO RouteManager doesn't use props, can they be removed ? */}
133
153
  <RouteManager pathname={pathname} screenData={screenData} />
134
154
  </OfflineFallbackScreen>
@@ -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
  }
@@ -78,7 +78,7 @@ export const getStyles = (config): Partial<TVTabsStyles> => {
78
78
  },
79
79
  tvos: {
80
80
  marginTop: -1,
81
- }
81
+ },
82
82
  }),
83
83
  },
84
84
  tabsBackground: {
@@ -7,6 +7,7 @@ import {
7
7
  } from "react-native";
8
8
  import { useSafeAreaFrame } from "react-native-safe-area-context";
9
9
  import { PathnameContext } from "../../Contexts/PathnameContext";
10
+ import { ScreenDataContext } from "../../Contexts/ScreenDataContext";
10
11
 
11
12
  type Props = {
12
13
  children: any;
@@ -16,6 +17,8 @@ type Props = {
16
17
  animating: boolean;
17
18
  contentStyle: ViewStyle;
18
19
  pathname: string;
20
+ isActive: boolean;
21
+ screenData: NavigationScreenData;
19
22
  };
20
23
 
21
24
  /**
@@ -38,34 +41,36 @@ export function Scene({
38
41
  animating,
39
42
  contentStyle,
40
43
  pathname,
44
+ isActive = false,
45
+ screenData,
41
46
  }: Props) {
47
+ const [initialScreenData, setInitialScreenData] = React.useState(screenData);
48
+
49
+ React.useLayoutEffect(() => {
50
+ if (isActive) {
51
+ setInitialScreenData(screenData);
52
+ }
53
+ }, [screenData, isActive]);
54
+
42
55
  const frame = useSafeAreaFrame();
43
56
  const { height, width } = useWindowDimensions();
44
57
 
45
- if (overlayStyle && animating) {
46
- // Safe frame takes too long to update,
47
- // so we can not rely on it there, and use height and width instead.
48
- return (
49
- <Animated.View
50
- pointerEvents={pointerEvents}
51
- style={[style, contentStyle, { height, width }]}
52
- >
53
- <PathnameContext.Provider value={pathname}>
54
- {children}
55
- </PathnameContext.Provider>
56
- <Animated.View style={overlayStyle} />
57
- </Animated.View>
58
- );
59
- }
58
+ // Safe frame takes too long to update,
59
+ // so we can not rely on it there, and use height and width instead.
60
+ const isAnimating = animating && overlayStyle;
61
+ const dimensions = isAnimating ? { height, width } : frame;
60
62
 
61
63
  return (
62
64
  <Animated.View
63
65
  pointerEvents={pointerEvents}
64
- style={[style, contentStyle, frame]}
66
+ style={[style, contentStyle, dimensions]}
65
67
  >
66
- <PathnameContext.Provider value={pathname}>
67
- {children}
68
- </PathnameContext.Provider>
68
+ <ScreenDataContext.Provider value={initialScreenData}>
69
+ <PathnameContext.Provider value={pathname}>
70
+ {children}
71
+ </PathnameContext.Provider>
72
+ </ScreenDataContext.Provider>
73
+ {isAnimating && <Animated.View style={overlayStyle} />}
69
74
  </Animated.View>
70
75
  );
71
76
  }
@@ -278,7 +278,8 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
278
278
  // instead of dangerous nulls or duplicates.
279
279
  renderSingleScene({ scenes, to }) {
280
280
  const { contentStyle, layoutData } = this.props;
281
- const screenData = scenes[to.index]?.children?.props?.screenData;
281
+ const screenData = this.props.children.props.screenData;
282
+ const pathname = scenes[to.index].key;
282
283
 
283
284
  const orientation = getScreenOrientation({
284
285
  screenData,
@@ -296,10 +297,12 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
296
297
  }) && (
297
298
  <Scene
298
299
  {...{ style: to.style }}
299
- pathname={scenes[to.index].key}
300
+ pathname={pathname}
301
+ isActive={pathname === this.props.navigator.currentRoute}
300
302
  contentStyle={contentStyle}
301
303
  key={scenes[to.index].key}
302
304
  screenUniqueId={scenes[to.index].screenUniqueId}
305
+ screenData={screenData}
303
306
  >
304
307
  {scenes[to.index].children}
305
308
  </Scene>
@@ -319,12 +322,14 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
319
322
  <Scene
320
323
  key={scenes[from.index].key}
321
324
  pathname={scenes[from.index].key}
325
+ isActive={scenes[from.index].key === this.props.navigator.currentRoute}
322
326
  style={from.style}
323
327
  pointerEvents="none"
324
328
  overlayStyle={from.overlayStyle}
325
329
  animating={this.state.animating}
326
330
  contentStyle={contentStyle}
327
331
  screenUniqueId={scenes[from.index].screenUniqueId}
332
+ screenData={scenes[from.index].children.props.screenData}
328
333
  >
329
334
  {scenes[from.index].children}
330
335
  </Scene>
@@ -333,12 +338,14 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
333
338
  const toScene = scenes[to.index] && (
334
339
  <Scene
335
340
  pathname={scenes[to.index].key}
341
+ isActive={scenes[to.index].key === this.props.navigator.currentRoute}
336
342
  key={scenes[to.index].key}
337
343
  style={to.style}
338
344
  overlayStyle={to.overlayStyle}
339
345
  animating={this.state.animating}
340
346
  contentStyle={contentStyle}
341
347
  screenUniqueId={scenes[to.index].screenUniqueId}
348
+ screenData={this.props.children.props.screenData}
342
349
  >
343
350
  {scenes[to.index].children}
344
351
  </Scene>
@@ -21,7 +21,9 @@ exports[`<Scene /> renders correctly 1`] = `
21
21
  ]
22
22
  }
23
23
  >
24
- <ContextProvider />
24
+ <ContextProvider>
25
+ <ContextProvider />
26
+ </ContextProvider>
25
27
  </ForwardRef(AnimatedComponentWrapper)>
26
28
  `;
27
29
 
@@ -43,7 +45,9 @@ exports[`<Scene /> renders the overlay while animating 1`] = `
43
45
  ]
44
46
  }
45
47
  >
46
- <ContextProvider />
48
+ <ContextProvider>
49
+ <ContextProvider />
50
+ </ContextProvider>
47
51
  <ForwardRef(AnimatedComponentWrapper)
48
52
  style={
49
53
  Object {