@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5251355865 → 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 (112) hide show
  1. package/Components/BaseFocusable/index.tsx +3 -3
  2. package/Components/Cell/Cell.tsx +7 -7
  3. package/Components/Cell/CellWithFocusable.tsx +40 -19
  4. package/Components/Cell/TvOSCellComponent.tsx +5 -9
  5. package/Components/ErrorScreen/index.tsx +1 -1
  6. package/Components/Focusable/Focusable.tsx +6 -1
  7. package/Components/FocusableCell/index.tsx +7 -7
  8. package/Components/FocusableGroup/index.tsx +11 -2
  9. package/Components/FocusableList/FocusableChild.ts +24 -14
  10. package/Components/FocusableList/FocusableItem.tsx +1 -1
  11. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  12. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  13. package/Components/FocusableList/index.tsx +26 -25
  14. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +6 -3
  15. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  16. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  17. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  18. package/Components/Image/Image.android.tsx +3 -25
  19. package/Components/Image/Image.tsx +18 -2
  20. package/Components/Image/utils/index.ts +39 -0
  21. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  22. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -10
  23. package/Components/Layout/TV/__tests__/index.test.tsx +19 -75
  24. package/Components/Layout/TV/index.tsx +12 -11
  25. package/Components/Layout/TV/index.web.tsx +33 -0
  26. package/Components/Layout/index.tsx +15 -11
  27. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  28. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -112
  29. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  30. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  31. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  32. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  33. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  34. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  35. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
  36. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  37. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  38. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  39. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  40. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  41. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  42. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  43. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  44. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  45. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  46. package/Components/MasterCell/utils/index.ts +33 -35
  47. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  48. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  49. package/Components/Navigator/StackNavigator.tsx +54 -0
  50. package/Components/Navigator/index.ts +1 -0
  51. package/Components/PlayerContainer/PlayerContainer.tsx +69 -31
  52. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  53. package/Components/PlayerImageBackground/index.tsx +34 -8
  54. package/Components/River/ComponentsMap/ComponentsMap.tsx +92 -22
  55. package/Components/River/RefreshControl.tsx +55 -1
  56. package/Components/River/TV/River.tsx +2 -25
  57. package/Components/River/__tests__/componentsMap.test.js +7 -0
  58. package/Components/River/useScreenConfiguration.ts +2 -2
  59. package/Components/Screen/TV/hooks/index.ts +3 -0
  60. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  61. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  62. package/Components/Screen/TV/index.web.tsx +186 -0
  63. package/Components/Screen/__tests__/Screen.test.tsx +8 -1
  64. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +14 -0
  65. package/Components/Screen/index.tsx +78 -30
  66. package/Components/ScreenResolver/index.tsx +3 -1
  67. package/Components/Tabs/TV/styles.ts +1 -1
  68. package/Components/Transitioner/Scene.tsx +24 -19
  69. package/Components/Transitioner/Transitioner.tsx +21 -28
  70. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +6 -2
  71. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  72. package/Components/VideoLive/LiveImageManager.ts +28 -1
  73. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -28
  74. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  75. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +18 -28
  76. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
  77. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +352 -0
  78. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +158 -0
  79. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +455 -0
  80. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +162 -0
  81. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  82. package/Components/VideoModal/ModalAnimation/utils.ts +301 -0
  83. package/Components/VideoModal/PlayerDetails.tsx +3 -3
  84. package/Components/VideoModal/PlayerWrapper.tsx +77 -47
  85. package/Components/VideoModal/VideoModal.tsx +23 -40
  86. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -1
  87. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +157 -105
  88. package/Components/VideoModal/utils.ts +8 -5
  89. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  90. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  91. package/Components/Viewport/utils.js +1 -1
  92. package/Components/ZappUIComponent/index.tsx +22 -12
  93. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  94. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  95. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  96. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  97. package/Contexts/FocusableGroupContext/index.ts +7 -0
  98. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  99. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  100. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  101. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  102. package/Contexts/ScreenContext/index.tsx +94 -28
  103. package/Contexts/ScreenDataContext/index.tsx +20 -0
  104. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  105. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  106. package/events/index.ts +1 -0
  107. package/package.json +5 -5
  108. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  109. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  110. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  111. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  112. /package/Contexts/{index.js → index.ts} +0 -0
@@ -1,10 +1,10 @@
1
1
  import * as R from "ramda";
2
2
 
3
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
4
3
  import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
5
4
  import { toCamelCase } from "@applicaster/zapp-react-native-utils/stringUtils";
6
5
  import { useMemo } from "react";
7
6
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
7
+ import { useRivers } from "@applicaster/zapp-react-native-utils/reactHooks/state";
8
8
 
9
9
  type GeneralContentScreen = ZappRiver & {
10
10
  styles: {
@@ -46,7 +46,7 @@ const stylingKeys = [
46
46
  ];
47
47
 
48
48
  export function useScreenConfiguration(screenId: string): ScreenConfiguration {
49
- const { rivers } = usePickFromState(["rivers"]);
49
+ const rivers = useRivers();
50
50
  const isTablet = useIsTablet();
51
51
 
52
52
  return useMemo(() => {
@@ -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,186 @@
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
+ export const isPresentFullScreen = pathOr(false, [
46
+ "state",
47
+ "screen",
48
+ "module",
49
+ "presentFullScreen",
50
+ ]);
51
+
52
+ const isPlayerPlugin = (routeState): boolean =>
53
+ getPluginType(routeState) === "player";
54
+
55
+ const isLoginPlugin = (routeState): boolean =>
56
+ getPluginType(routeState) === "login";
57
+
58
+ const getActiveRiver = ({
59
+ screenId,
60
+ screenType,
61
+ routeState,
62
+ rivers,
63
+ fallbackActiveRiver,
64
+ }) => {
65
+ if (rivers[screenId]) {
66
+ if (screenType === "hooks") {
67
+ const navigations = routeState?.state?.entry?.payload?.navigations;
68
+
69
+ if (navigations) {
70
+ return routeState.state.entry.payload;
71
+ }
72
+ }
73
+
74
+ return routeState?.state?.screen;
75
+ }
76
+
77
+ // screenId is not presented into rivers, use fallback instead
78
+ return fallbackActiveRiver;
79
+ };
80
+
81
+ const getNavigations = ({
82
+ screenId,
83
+ screenType,
84
+ routeState,
85
+ rivers,
86
+ fallbackNavigations,
87
+ }) => {
88
+ if (rivers[screenId]) {
89
+ // river presents
90
+ if (screenType === "hooks") {
91
+ const navigations = routeState?.state?.entry?.payload?.navigations;
92
+
93
+ if (navigations) {
94
+ return navigations;
95
+ }
96
+ }
97
+
98
+ return routeState?.state?.screen?.navigations;
99
+ }
100
+
101
+ // screenId is not presented into rivers, use fallback instead
102
+ return fallbackNavigations;
103
+ };
104
+
105
+ export const Screen = ({ route, Components }: Props) => {
106
+ if (isNilOrEmpty(route)) {
107
+ throw Error("Required props: route is missing");
108
+ }
109
+
110
+ const navigator = useNavigation();
111
+ const { plugins = [], rivers = [] } = usePickFromState(["plugins", "rivers"]);
112
+
113
+ const pathAttributes = getPathAttributes({ pathname: route });
114
+ const routeState = navigator.getStackForPathname(route);
115
+
116
+ const { screenType, screenId } = last(pathAttributes);
117
+
118
+ const backgroundColor = useScreenBackgroundColor(screenId);
119
+
120
+ useInitialFocus();
121
+ useRestoreFocus();
122
+
123
+ const activeRiver = getActiveRiver({
124
+ screenId,
125
+ screenType,
126
+ routeState,
127
+ rivers,
128
+ fallbackActiveRiver: navigator.activeRiver,
129
+ });
130
+
131
+ const navigationProps = getNavigationProps({
132
+ navigator: {
133
+ // @ts-ignore
134
+ activeRiver,
135
+ // @ts-ignore
136
+ screenTitle: routeState.state.entry?.title || routeState.screen?.name,
137
+ },
138
+ category: "menu",
139
+ });
140
+
141
+ const navigations = getNavigations({
142
+ screenId,
143
+ screenType,
144
+ routeState,
145
+ rivers,
146
+ fallbackNavigations: navigator.activeRiver.navigations,
147
+ });
148
+
149
+ const menuPlugin = resolveNavigationPlugin({
150
+ category: "menu",
151
+ navigations,
152
+ plugins,
153
+ }) as QuickBrickPlugin;
154
+
155
+ const NavBar = (Components.NavBar ||
156
+ menuPlugin.module) as React.ComponentType<any>;
157
+
158
+ const isPlayer = isPlayerPlugin(routeState);
159
+ const isLogin = isLoginPlugin(routeState);
160
+ const isFullScreen = isPresentFullScreen(routeState);
161
+
162
+ const isNavBarVisible = !isPlayer && !isLogin && !isFullScreen;
163
+
164
+ const screenData =
165
+ screenType === "hooks" || isPlayer
166
+ ? routeState?.state?.entry
167
+ : routeState?.state?.screen;
168
+
169
+ return (
170
+ <View style={[styles.container, { backgroundColor }]}>
171
+ <NavBarContainer isVisible={isNavBarVisible} onReady={noop}>
172
+ <NavBar
173
+ isVisible={isNavBarVisible}
174
+ menuPlugin={menuPlugin}
175
+ navigationProps={navigationProps}
176
+ />
177
+ </NavBarContainer>
178
+ <ScreenResolver
179
+ screenType={screenType}
180
+ screenId={screenId}
181
+ screenData={screenData}
182
+ groupId={route}
183
+ />
184
+ </View>
185
+ );
186
+ };
@@ -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} />
@@ -10,6 +10,8 @@ const mock_navBarVisibleFlag = true;
10
10
 
11
11
  const mockIsNavBarVisible = jest.fn(() => mock_navBarVisibleFlag);
12
12
 
13
+ const mockIsOrientationCompatible = jest.fn(() => true);
14
+
13
15
  jest.mock("react-native-safe-area-context", () => ({
14
16
  useSafeAreaInsets: () => ({ top: 44 }),
15
17
  }));
@@ -21,6 +23,7 @@ jest.mock("../../RouteManager", () => ({
21
23
  jest.mock(
22
24
  "@applicaster/zapp-react-native-utils/appUtils/orientationHelper",
23
25
  () => ({
26
+ isOrientationCompatible: mockIsOrientationCompatible,
24
27
  allowedOrientationsForScreen: jest.fn(),
25
28
  getOrientation: jest.fn(),
26
29
  getScreenOrientation: jest.fn(),
@@ -69,12 +72,16 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
69
72
  isNavBarVisible: mockIsNavBarVisible,
70
73
  useRoute: jest.fn(() => ({
71
74
  pathname: "/river/testId",
75
+ screenData: { id: "testId" },
72
76
  })),
73
77
  useNavigation: jest.fn(() => ({
74
78
  canGoBack: () => false,
75
79
  currentRoute: "/river/testId",
80
+ activeRiver: { id: "testId" },
81
+ screenData: { id: "testId" },
76
82
  data: { screen: { id: "testId" } },
77
83
  })),
84
+ useIsScreenActive: jest.fn().mockReturnValue(true),
78
85
  }));
79
86
 
80
87
  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>
@@ -3,10 +3,17 @@ import React from "react";
3
3
  import * as R from "ramda";
4
4
  import { View } from "react-native";
5
5
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
6
+ import {
7
+ isOrientationCompatible,
8
+ useGetScreenOrientation,
9
+ } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
6
10
 
7
11
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
8
12
  import { getComponentModule } from "@applicaster/zapp-react-native-utils/pluginUtils";
9
- import { getNavigationPropsV2 } from "@applicaster/zapp-react-native-utils/navigationUtils";
13
+ import {
14
+ getNavigationPropsV2,
15
+ resolveNavigationPlugin,
16
+ } from "@applicaster/zapp-react-native-utils/navigationUtils";
10
17
  import { useSafeAreaInsets } from "react-native-safe-area-context";
11
18
  import {
12
19
  useDimensions,
@@ -14,6 +21,7 @@ import {
14
21
  useCurrentScreenData,
15
22
  useNavbarState,
16
23
  useScreenData,
24
+ useNavigation,
17
25
  } from "@applicaster/zapp-react-native-utils/reactHooks";
18
26
  import { getNavigationPluginModule } from "@applicaster/zapp-react-native-app/App/Layout/layoutHelpers";
19
27
 
@@ -22,6 +30,17 @@ import { RouteManager } from "../RouteManager";
22
30
  import { useScreenConfiguration } from "../River/useScreenConfiguration";
23
31
  import { useScreenOrientationHandler } from "./orientationHandler";
24
32
 
33
+ const getScreenId = (
34
+ screenData: LegacyNavigationScreenData,
35
+ fallbackRiver: ZappRiver
36
+ ) => {
37
+ return screenData?.targetScreen
38
+ ? screenData?.targetScreen?.id ?? fallbackRiver.id
39
+ : "screen_id" in screenData
40
+ ? screenData.screen_id
41
+ : fallbackRiver.id;
42
+ };
43
+
25
44
  function colorIsNotTransparent(color) {
26
45
  const layers = R.compose(
27
46
  R.map(R.trim),
@@ -40,14 +59,13 @@ function isValidColor(string) {
40
59
  }
41
60
 
42
61
  type Props = {
43
- pathname: string;
62
+ pathname: unknown;
44
63
  screenData: unknown;
45
- setToggleMenu: () => void;
46
- setCloseMenu: () => void;
47
64
  };
48
65
 
49
- export function Screen({ setToggleMenu, setCloseMenu }: Props) {
66
+ export function Screen(_props: Props) {
50
67
  const theme = useTheme<BaseThemePropertiesMobile>();
68
+ const navigation = useNavigation();
51
69
  const { plugins } = usePickFromState(["plugins"]);
52
70
 
53
71
  // Gets the data for the current screen configuration
@@ -55,32 +73,53 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
55
73
  const { backgroundColor } = useScreenConfiguration(currentScreenData.id);
56
74
  const insets = useSafeAreaInsets();
57
75
 
58
- const { width: screenWidth } = useDimensions("screen", {
76
+ const {
77
+ width: screenWidth,
78
+ height,
79
+ deviceInfo,
80
+ } = useDimensions("screen", {
59
81
  fullDimensions: true,
60
82
  });
61
83
 
62
- const routeData = useRoute();
84
+ const { screenData, pathname } = useRoute();
63
85
 
64
86
  const currentRiver = useScreenData(
65
- (routeData.screenData as ZappRiver)?.targetScreen
66
- ? (routeData.screenData as ZappRiver)?.targetScreen.id
67
- : (routeData.screenData as ZappRiver)?.screen_id
87
+ getScreenId(screenData, navigation.activeRiver)
68
88
  );
69
89
 
70
90
  const { title } = useNavbarState();
71
91
 
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
- );
92
+ const isTablet = deviceInfo?.isTablet;
93
+
94
+ const { appData } = usePickFromState(["appData"]);
95
+ const isTabletPortrait = appData?.isTabletPortrait;
96
+
97
+ const layoutData = { isTablet, isTabletPortrait, width: screenWidth, height };
98
+
99
+ const hasMenu =
100
+ resolveNavigationPlugin({
101
+ category: "menu",
102
+ navigations: currentRiver.navigations,
103
+ plugins,
104
+ })?.identifier === "quick_brick_side_menu";
105
+
106
+ const navBarProps = React.useMemo<MobileNavBarPluginProps | null>(() => {
107
+ const props = getNavigationPropsV2({
108
+ currentRiver,
109
+ title,
110
+ category: "nav_bar",
111
+ });
112
+
113
+ if (props) {
114
+ return {
115
+ ...props,
116
+ id: pathname,
117
+ pathname: pathname,
118
+ };
119
+ }
120
+
121
+ return null;
122
+ }, [currentRiver, pathname]);
84
123
 
85
124
  const NavBar = React.useMemo(
86
125
  () =>
@@ -88,12 +127,10 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
88
127
  "nav_bar",
89
128
  currentRiver,
90
129
  plugins
91
- ) as React.ComponentType<MobileNavBarPluginProps>,
130
+ ) as React.ComponentType<MobileNavBarPluginProps & { hasMenu: boolean }>,
92
131
  []
93
132
  );
94
133
 
95
- const { screenData, pathname } = routeData;
96
-
97
134
  const offlinePlugin = getComponentModule({
98
135
  plugins,
99
136
  components: {},
@@ -121,15 +158,26 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
121
158
 
122
159
  useScreenOrientationHandler({ screenData: targetScreenData });
123
160
 
161
+ const orientation = useGetScreenOrientation(targetScreenData);
162
+
163
+ // We prevent rendering of the screen until UI is actually rotated to screen desired orientation.
164
+ // This saves unnecessary re-renders and user will not see distorted aspect screen.
165
+ if (
166
+ !isOrientationCompatible({
167
+ orientation,
168
+ layoutData,
169
+ })
170
+ ) {
171
+ return null;
172
+ }
173
+
124
174
  return (
125
175
  <ScreenContextProvider pathname={pathname}>
126
176
  <View style={style}>
127
- <NavBar
128
- {...{ ...(navBarProps as MobileNavBarPluginProps), id: pathname }}
129
- />
177
+ {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
130
178
 
131
- <OfflineFallbackScreen screenData={screenData}>
132
- {/* RouteManager don't need these props, can it be removed ? */}
179
+ <OfflineFallbackScreen screenData={targetScreenData}>
180
+ {/* @TODO RouteManager doesn't use props, can they be removed ? */}
133
181
  <RouteManager pathname={pathname} screenData={screenData} />
134
182
  </OfflineFallbackScreen>
135
183
  </View>
@@ -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
  }