@applicaster/zapp-react-native-ui-components 15.0.0-rc.12 → 15.0.0-rc.121

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 (159) hide show
  1. package/Components/AnimatedInOut/index.tsx +69 -26
  2. package/Components/BaseFocusable/index.ios.ts +12 -2
  3. package/Components/Cell/Cell.tsx +14 -3
  4. package/Components/Cell/CellWithFocusable.tsx +9 -0
  5. package/Components/Cell/FocusableWrapper.tsx +47 -0
  6. package/Components/Cell/TvOSCellComponent.tsx +106 -19
  7. package/Components/Focusable/Focusable.tsx +4 -2
  8. package/Components/Focusable/FocusableTvOS.tsx +18 -1
  9. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
  10. package/Components/FocusableGroup/FocusableTvOS.tsx +32 -1
  11. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
  12. package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
  13. package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
  14. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  15. package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
  16. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +22 -6
  17. package/Components/HandlePlayable/HandlePlayable.tsx +33 -94
  18. package/Components/HandlePlayable/const.ts +3 -0
  19. package/Components/HandlePlayable/utils.ts +105 -0
  20. package/Components/HookRenderer/HookRenderer.tsx +40 -10
  21. package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
  22. package/Components/Layout/TV/LayoutBackground.tsx +5 -2
  23. package/Components/Layout/TV/NavBarContainer.tsx +1 -10
  24. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  25. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
  26. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
  27. package/Components/Layout/TV/index.tsx +3 -4
  28. package/Components/Layout/TV/index.web.tsx +3 -4
  29. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  30. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +16 -1
  31. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +30 -2
  32. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +5 -1
  33. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  34. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  35. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  36. package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
  37. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
  38. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +43 -22
  39. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +40 -39
  40. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +95 -0
  41. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +86 -0
  42. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/index.test.ts +141 -0
  43. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  44. package/Components/MasterCell/DefaultComponents/SecondaryImage/index.ts +1 -1
  45. package/Components/MasterCell/DefaultComponents/Text/index.tsx +8 -8
  46. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -2
  47. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +233 -11
  48. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +19 -15
  49. package/Components/MasterCell/hoc/__tests__/withAsyncRender.test.tsx +219 -0
  50. package/Components/MasterCell/hoc/withAsyncRender.tsx +9 -7
  51. package/Components/MasterCell/index.tsx +2 -0
  52. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +82 -3
  53. package/Components/MasterCell/utils/index.ts +61 -31
  54. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +102 -87
  55. package/Components/MeasurmentsPortal/__tests__/MeasurementsPortal.test.tsx +355 -0
  56. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +17 -9
  57. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +16 -8
  58. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
  59. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
  60. package/Components/OfflineHandler/NotificationView/utils.ts +34 -0
  61. package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
  62. package/Components/PlayerContainer/PlayerContainer.tsx +43 -64
  63. package/Components/PlayerImageBackground/index.tsx +3 -22
  64. package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
  65. package/Components/PreloaderWrapper/index.tsx +15 -0
  66. package/Components/River/ComponentsMap/ComponentsMap.tsx +16 -0
  67. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  68. package/Components/River/RefreshControl.tsx +9 -3
  69. package/Components/River/RiverItem.tsx +26 -20
  70. package/Components/River/TV/River.tsx +31 -14
  71. package/Components/River/TV/index.tsx +8 -4
  72. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  73. package/Components/River/TV/utils/index.ts +4 -0
  74. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  75. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  76. package/Components/River/__tests__/componentsMap.test.js +38 -0
  77. package/Components/Screen/TV/index.web.tsx +4 -2
  78. package/Components/Screen/__tests__/Screen.test.tsx +66 -42
  79. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  80. package/Components/Screen/hooks.ts +75 -6
  81. package/Components/Screen/index.tsx +9 -4
  82. package/Components/Screen/navigationHandler.ts +49 -24
  83. package/Components/Screen/orientationHandler.ts +10 -13
  84. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
  85. package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
  86. package/Components/ScreenFeedLoader/index.ts +1 -0
  87. package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
  88. package/Components/ScreenResolver/hooks/index.ts +3 -0
  89. package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
  90. package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
  91. package/Components/ScreenResolver/index.tsx +15 -111
  92. package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
  93. package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
  94. package/Components/ScreenResolver/utils/index.ts +1 -0
  95. package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
  96. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
  97. package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
  98. package/Components/ScreenResolverFeedProvider/index.ts +1 -0
  99. package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
  100. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
  101. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +44 -26
  102. package/Components/Tabs/TV/Tabs.tsx +20 -3
  103. package/Components/Tabs/TabContent.tsx +7 -4
  104. package/Components/Transitioner/Scene.tsx +10 -3
  105. package/Components/Transitioner/index.js +3 -3
  106. package/Components/VideoLive/LiveImageManager.ts +199 -54
  107. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -33
  108. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
  109. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
  110. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +118 -171
  111. package/Components/VideoModal/ModalAnimation/index.ts +2 -13
  112. package/Components/VideoModal/ModalAnimation/utils.ts +1 -327
  113. package/Components/VideoModal/PlayerWrapper.tsx +14 -88
  114. package/Components/VideoModal/VideoModal.tsx +1 -5
  115. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
  116. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  117. package/Components/VideoModal/hooks/useModalSize.ts +10 -5
  118. package/Components/VideoModal/playerWrapperStyle.ts +70 -0
  119. package/Components/VideoModal/playerWrapperUtils.ts +91 -0
  120. package/Components/VideoModal/utils.ts +19 -9
  121. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  122. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  123. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  124. package/Components/ZappUIComponent/index.tsx +12 -6
  125. package/Components/index.js +1 -1
  126. package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
  127. package/Contexts/ScreenContext/index.tsx +71 -19
  128. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  129. package/Contexts/ZappHookModalContext/index.tsx +37 -61
  130. package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
  131. package/Contexts/index.ts +0 -2
  132. package/Decorators/Analytics/index.tsx +6 -5
  133. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  134. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  135. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
  136. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
  137. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  138. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  139. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  140. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  141. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  142. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  143. package/Helpers/DataSourceHelper/index.ts +19 -0
  144. package/events/index.ts +3 -0
  145. package/events/scrollEndReached.ts +15 -0
  146. package/index.d.ts +7 -0
  147. package/package.json +6 -5
  148. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
  149. package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
  150. package/Components/River/TV/withTVEventHandler.tsx +0 -27
  151. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +0 -60
  152. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +0 -417
  153. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +0 -294
  154. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +0 -176
  155. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +0 -93
  156. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +0 -500
  157. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +0 -108
  158. package/Helpers/DataSourceHelper/index.js +0 -19
  159. /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
@@ -137,6 +137,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
137
137
  keyExtractor={[Function]}
138
138
  maxToRenderPerBatch={10}
139
139
  onContentSizeChange={[Function]}
140
+ onEndReached={[Function]}
140
141
  onLayout={[Function]}
141
142
  onMomentumScrollBegin={[Function]}
142
143
  onMomentumScrollEnd={[Function]}
@@ -154,6 +155,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
154
155
  }
155
156
  }
156
157
  stickyHeaderIndices={[]}
158
+ testID="components-map-flat-list"
157
159
  viewabilityConfigCallbackPairs={[]}
158
160
  windowSize={12}
159
161
  >
@@ -139,7 +139,13 @@ jest.mock(
139
139
  })
140
140
  );
141
141
 
142
+ jest.mock("@applicaster/zapp-react-native-ui-components/events", () => ({
143
+ ...jest.requireActual("@applicaster/zapp-react-native-ui-components/events"),
144
+ emitScrollEndReached: jest.fn(),
145
+ }));
146
+
142
147
  const { View } = require("react-native");
148
+ const events = require("@applicaster/zapp-react-native-ui-components/events");
143
149
  const { ComponentsMap } = require("../ComponentsMap/ComponentsMap");
144
150
  const theme = require("./theme-mock.json");
145
151
 
@@ -190,4 +196,36 @@ describe("componentsMap", () => {
190
196
 
191
197
  expect(toJSON()).toMatchSnapshot();
192
198
  });
199
+
200
+ it("calls emitScrollEndReached when onScroll was called and isScreenWrappedInContainer is false", () => {
201
+ themeSpy = jest
202
+ .spyOn(themeUtils, "useTheme")
203
+ .mockImplementation(() => () => theme);
204
+
205
+ events.emitScrollEndReached.mockClear();
206
+
207
+ const { getByTestId } = render(
208
+ <Provider store={store}>
209
+ <ComponentsMap
210
+ {...props}
211
+ isScreenWrappedInContainer={false}
212
+ feed={{ entry: [] }}
213
+ />
214
+ </Provider>
215
+ );
216
+
217
+ const flatList = getByTestId("components-map-flat-list");
218
+
219
+ flatList.props.onScroll({
220
+ nativeEvent: {
221
+ contentOffset: { y: 0 },
222
+ layoutMeasurement: { height: 100 },
223
+ contentSize: { height: 200 },
224
+ },
225
+ });
226
+
227
+ flatList.props.onEndReached();
228
+
229
+ expect(events.emitScrollEndReached).toHaveBeenCalledTimes(1);
230
+ });
193
231
  });
@@ -9,9 +9,10 @@ import {
9
9
  import {
10
10
  useIsScreenActive,
11
11
  useNavigation,
12
+ useRivers,
12
13
  } from "@applicaster/zapp-react-native-utils/reactHooks";
13
14
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
14
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
15
+ import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
15
16
  import {
16
17
  useNavbarState,
17
18
  useScreenBackgroundColor,
@@ -102,7 +103,8 @@ export const Screen = ({ route, Components }: Props) => {
102
103
  }
103
104
 
104
105
  const navigator = useNavigation();
105
- const { plugins = [], rivers = [] } = usePickFromState(["plugins", "rivers"]);
106
+ const plugins = usePlugins();
107
+ const rivers = useRivers();
106
108
 
107
109
  const pathAttributes = getPathAttributes({ pathname: route });
108
110
  const routeState = navigator.getStackForPathname(route);
@@ -1,19 +1,17 @@
1
1
  import * as React from "react";
2
2
  import { View } from "react-native";
3
- import { render } from "@testing-library/react-native";
3
+ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
4
4
 
5
5
  const Mocked_RouteManager = jest.fn((props) => (
6
6
  <View testID="routeManager" {...props} />
7
7
  ));
8
8
 
9
- const mock_navBarVisibleFlag = true;
10
-
11
- const mockIsNavBarVisible = jest.fn(() => mock_navBarVisibleFlag);
12
-
13
9
  const mockIsOrientationCompatible = jest.fn(() => true);
14
10
 
15
11
  jest.mock("react-native-safe-area-context", () => ({
12
+ ...jest.requireActual("react-native-safe-area-context"),
16
13
  useSafeAreaInsets: () => ({ top: 44 }),
14
+ useSafeAreaFrame: () => ({ x: 0, y: 0, width: 375, height: 812 }),
17
15
  }));
18
16
 
19
17
  jest.mock("../../RouteManager", () => ({
@@ -35,12 +33,14 @@ jest.mock(
35
33
  );
36
34
 
37
35
  jest.mock("@applicaster/zapp-react-native-utils/analyticsUtils", () => ({
36
+ ...jest.requireActual("@applicaster/zapp-react-native-utils/analyticsUtils"),
38
37
  useAnalytics: jest.fn(() => ({
39
38
  sendScreenEvent: jest.fn(),
40
39
  })),
41
40
  }));
42
41
 
43
42
  jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
43
+ ...jest.requireActual("@applicaster/zapp-react-native-utils/theme"),
44
44
  useTheme: jest.fn(() => ({
45
45
  app_background_color: "blue",
46
46
  })),
@@ -77,21 +77,44 @@ jest.mock(
77
77
  })
78
78
  );
79
79
 
80
- jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
81
- isNavBarVisible: mockIsNavBarVisible,
82
- useIsScreenActive: jest.fn().mockReturnValue(true),
83
- useNavigation: jest.fn(() => ({
84
- canGoBack: () => false,
85
- currentRoute: "/river/testId",
86
- activeRiver: { id: "testId" },
87
- screenData: { id: "testId" },
88
- data: { screen: { id: "testId" } },
89
- })),
90
- useRoute: jest.fn(() => ({
91
- pathname: "/river/testId",
92
- screenData: { id: "testId" },
93
- })),
94
- }));
80
+ jest.mock(
81
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation",
82
+ () => ({
83
+ ...jest.requireActual(
84
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation"
85
+ ),
86
+ useNavigation: jest.fn(() => ({
87
+ canGoBack: () => false,
88
+ currentRoute: "/river/testId",
89
+ activeRiver: { id: "testId" },
90
+ screenData: { id: "testId" },
91
+ data: { screen: { id: "testId" } },
92
+ })),
93
+ })
94
+ );
95
+
96
+ jest.mock(
97
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useIsScreenActive",
98
+ () => ({
99
+ ...jest.requireActual(
100
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useIsScreenActive"
101
+ ),
102
+ useIsScreenActive: jest.fn().mockReturnValue(true),
103
+ })
104
+ );
105
+
106
+ jest.mock(
107
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute",
108
+ () => ({
109
+ ...jest.requireActual(
110
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute"
111
+ ),
112
+ useRoute: jest.fn(() => ({
113
+ pathname: "/river/testId",
114
+ screenData: { id: "testId" },
115
+ })),
116
+ })
117
+ );
95
118
 
96
119
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
97
120
  ...jest.requireActual("@applicaster/zapp-react-native-utils/reactHooks"),
@@ -112,26 +135,6 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
112
135
  useIsTablet: jest.fn(() => false),
113
136
  }));
114
137
 
115
- jest.mock("@applicaster/zapp-react-native-redux/hooks", () => {
116
- const View = jest.requireActual("react-native").View;
117
-
118
- return {
119
- ...jest.requireActual("@applicaster/zapp-react-native-redux/hooks"),
120
- usePickFromState: () => ({
121
- plugins: [
122
- {
123
- name: "Offline Plugin",
124
- identifier: "offline-experience",
125
- type: "general",
126
- module: {
127
- OfflineFallbackScreen: ({ children }) => <View>{children}</View>, // eslint-disable-line
128
- },
129
- },
130
- ],
131
- }),
132
- };
133
- });
134
-
135
138
  const {
136
139
  allowedOrientationsForScreen,
137
140
  getOrientation,
@@ -151,6 +154,19 @@ const screenProps = {
151
154
 
152
155
  const { Screen } = require("..");
153
156
 
157
+ const store = {
158
+ plugins: [
159
+ {
160
+ name: "Offline Plugin",
161
+ identifier: "offline-experience",
162
+ type: "general",
163
+ module: {
164
+ OfflineFallbackScreen: ({ children }) => <View>{children}</View>, // eslint-disable-line
165
+ },
166
+ },
167
+ ],
168
+ };
169
+
154
170
  describe("<Screen Component />", () => {
155
171
  beforeEach(() => {
156
172
  allowedOrientationsForScreen.mockClear();
@@ -160,14 +176,22 @@ describe("<Screen Component />", () => {
160
176
 
161
177
  describe("when the navbar should show", () => {
162
178
  it("renders correctly", () => {
163
- const { toJSON } = render(<Screen {...screenProps} />);
179
+ const { toJSON } = renderWithProviders(
180
+ <Screen {...screenProps} />,
181
+ store
182
+ );
183
+
164
184
  expect(toJSON()).toMatchSnapshot();
165
185
  });
166
186
  });
167
187
 
168
188
  describe("when the navbar should be hidden", () => {
169
189
  it("renders correctly", () => {
170
- const { toJSON } = render(<Screen {...screenProps} />);
190
+ const { toJSON } = renderWithProviders(
191
+ <Screen {...screenProps} />,
192
+ store
193
+ );
194
+
171
195
  expect(toJSON()).toMatchSnapshot();
172
196
  });
173
197
  });
@@ -1,67 +1,91 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<Screen Component /> when the navbar should be hidden renders correctly 1`] = `
4
- <View
5
- importantForAccessibility="yes"
4
+ <RNCSafeAreaProvider
5
+ onInsetsChange={[Function]}
6
6
  style={
7
- {
8
- "backgroundColor": "blue",
9
- "flex": 1,
10
- "paddingTop": 0,
11
- }
7
+ [
8
+ {
9
+ "flex": 1,
10
+ },
11
+ undefined,
12
+ ]
12
13
  }
13
14
  >
14
15
  <View
15
- hasMenu={false}
16
- id="/river/testId"
17
- pathname="/river/testId"
18
- selected="testId"
19
- testID="navBar"
20
- title="Test Title"
21
- />
22
- <View>
16
+ importantForAccessibility="yes"
17
+ style={
18
+ {
19
+ "backgroundColor": "blue",
20
+ "flex": 1,
21
+ "paddingTop": 0,
22
+ }
23
+ }
24
+ >
23
25
  <View
26
+ hasMenu={false}
27
+ id="/river/testId"
24
28
  pathname="/river/testId"
25
- screenData={
26
- {
27
- "id": "testId",
28
- }
29
- }
30
- testID="routeManager"
29
+ selected="testId"
30
+ testID="navBar"
31
+ title="Test Title"
31
32
  />
33
+ <View>
34
+ <View
35
+ pathname="/river/testId"
36
+ screenData={
37
+ {
38
+ "id": "testId",
39
+ }
40
+ }
41
+ testID="routeManager"
42
+ />
43
+ </View>
32
44
  </View>
33
- </View>
45
+ </RNCSafeAreaProvider>
34
46
  `;
35
47
 
36
48
  exports[`<Screen Component /> when the navbar should show renders correctly 1`] = `
37
- <View
38
- importantForAccessibility="yes"
49
+ <RNCSafeAreaProvider
50
+ onInsetsChange={[Function]}
39
51
  style={
40
- {
41
- "backgroundColor": "blue",
42
- "flex": 1,
43
- "paddingTop": 0,
44
- }
52
+ [
53
+ {
54
+ "flex": 1,
55
+ },
56
+ undefined,
57
+ ]
45
58
  }
46
59
  >
47
60
  <View
48
- hasMenu={false}
49
- id="/river/testId"
50
- pathname="/river/testId"
51
- selected="testId"
52
- testID="navBar"
53
- title="Test Title"
54
- />
55
- <View>
61
+ importantForAccessibility="yes"
62
+ style={
63
+ {
64
+ "backgroundColor": "blue",
65
+ "flex": 1,
66
+ "paddingTop": 0,
67
+ }
68
+ }
69
+ >
56
70
  <View
71
+ hasMenu={false}
72
+ id="/river/testId"
57
73
  pathname="/river/testId"
58
- screenData={
59
- {
60
- "id": "testId",
61
- }
62
- }
63
- testID="routeManager"
74
+ selected="testId"
75
+ testID="navBar"
76
+ title="Test Title"
64
77
  />
78
+ <View>
79
+ <View
80
+ pathname="/river/testId"
81
+ screenData={
82
+ {
83
+ "id": "testId",
84
+ }
85
+ }
86
+ testID="routeManager"
87
+ />
88
+ </View>
65
89
  </View>
66
- </View>
90
+ </RNCSafeAreaProvider>
67
91
  `;
@@ -1,19 +1,89 @@
1
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
1
+ import { useAppData } from "@applicaster/zapp-react-native-redux/hooks";
2
2
  import {
3
3
  useGetScreenOrientation,
4
4
  isOrientationCompatible,
5
5
  } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
6
6
  import {
7
7
  useCurrentScreenData,
8
- useDimensions,
9
8
  useRoute,
10
9
  useIsTablet,
10
+ useIsScreenActive,
11
11
  } from "@applicaster/zapp-react-native-utils/reactHooks";
12
12
  import { useMemo, useEffect, useState } from "react";
13
+ import { useSafeAreaFrame } from "react-native-safe-area-context";
14
+
15
+ type MemoizedSafeAreaFrameWithActiveStateOptions = {
16
+ updateForInactiveScreens?: boolean;
17
+ isActive: boolean;
18
+ };
19
+
20
+ /**
21
+ * Base hook that wraps useSafeAreaFrame with memoization and inactive screen filtering.
22
+ * Requires isActive to be passed explicitly - use useMemoizedSafeAreaFrame for automatic detection.
23
+ *
24
+ * @param options.updateForInactiveScreens - If false, frame won't update when screen is inactive (default: true)
25
+ * @param options.isActive - Whether the screen is currently active
26
+ * @returns The memoized safe area frame { x, y, width, height }
27
+ */
28
+ export const useMemoizedSafeAreaFrameWithActiveState = (
29
+ options: MemoizedSafeAreaFrameWithActiveStateOptions
30
+ ) => {
31
+ const { updateForInactiveScreens = true, isActive } = options;
32
+ const frame = useSafeAreaFrame();
33
+
34
+ const [memoFrame, setMemoFrame] = useState(frame);
35
+
36
+ useEffect(() => {
37
+ const shouldUpdate = isActive || updateForInactiveScreens;
38
+
39
+ const dimensionsChanged =
40
+ frame.width !== memoFrame.width || frame.height !== memoFrame.height;
41
+
42
+ if (shouldUpdate && dimensionsChanged) {
43
+ setMemoFrame(frame);
44
+ }
45
+ }, [
46
+ frame.width,
47
+ frame.height,
48
+ isActive,
49
+ updateForInactiveScreens,
50
+ frame,
51
+ memoFrame.width,
52
+ memoFrame.height,
53
+ ]);
54
+
55
+ return memoFrame;
56
+ };
57
+
58
+ type MemoizedSafeAreaFrameOptions = {
59
+ updateForInactiveScreens?: boolean;
60
+ };
61
+
62
+ /**
63
+ * Hook that wraps useSafeAreaFrame with memoization and inactive screen filtering.
64
+ * Uses useIsScreenActive() internally to determine active state - use useMemoizedSafeAreaFrameWithActiveState
65
+ * if you need to pass isActive explicitly.
66
+ *
67
+ * @param options.updateForInactiveScreens - If false, frame won't update when screen is inactive (default: true)
68
+ * @returns The memoized safe area frame { x, y, width, height }
69
+ */
70
+ export const useMemoizedSafeAreaFrame = (
71
+ options: MemoizedSafeAreaFrameOptions = {}
72
+ ) => {
73
+ const { updateForInactiveScreens = true } = options;
74
+ const isActive = useIsScreenActive();
75
+
76
+ return useMemoizedSafeAreaFrameWithActiveState({
77
+ updateForInactiveScreens,
78
+ isActive,
79
+ });
80
+ };
13
81
 
14
82
  export const useWaitForValidOrientation = () => {
15
- const { width: screenWidth, height } = useDimensions("screen", {
16
- fullDimensions: true,
83
+ // Use memoized safe area frame to synchronize with Scene's dimension source
84
+ // This prevents race conditions where the orientation check passes before
85
+ // Scene's memoFrame has updated to the new dimensions
86
+ const { width: screenWidth, height } = useMemoizedSafeAreaFrame({
17
87
  updateForInactiveScreens: false,
18
88
  });
19
89
 
@@ -25,8 +95,7 @@ export const useWaitForValidOrientation = () => {
25
95
 
26
96
  const isTablet = useIsTablet();
27
97
 
28
- const { appData } = usePickFromState(["appData"]);
29
- const isTabletPortrait = appData?.isTabletPortrait;
98
+ const { isTabletPortrait } = useAppData();
30
99
 
31
100
  const layoutData = useMemo(
32
101
  () => ({ isTablet, isTabletPortrait, width: screenWidth, height }),
@@ -1,8 +1,7 @@
1
1
  /// <reference types="@applicaster/applicaster-types" />
2
2
  import React from "react";
3
3
  import { AccessibilityInfo, findNodeHandle, View } from "react-native";
4
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
-
4
+ import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
6
5
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
7
6
  import { getComponentModule } from "@applicaster/zapp-react-native-utils/pluginUtils";
8
7
  import {
@@ -41,7 +40,7 @@ type Props = {
41
40
  export function Screen(_props: Props) {
42
41
  const theme = useTheme<BaseThemePropertiesMobile>();
43
42
  const navigation = useNavigation();
44
- const { plugins } = usePickFromState(["plugins"]);
43
+ const plugins = usePlugins();
45
44
 
46
45
  // Gets the data for the current screen configuration
47
46
  const currentScreenData = useCurrentScreenData();
@@ -93,7 +92,13 @@ export function Screen(_props: Props) {
93
92
  const isActive = useIsScreenActive();
94
93
 
95
94
  const ref = React.useRef(null);
96
- const isReady = useWaitForValidOrientation();
95
+ const isOrientationReady = useWaitForValidOrientation();
96
+
97
+ // Playable screens handle their own orientation via the native player plugin,
98
+ // so we skip the orientation wait gate to avoid a deadlock where the screen
99
+ // waits for landscape but blocks rendering that would trigger the rotation.
100
+ const isPlayableRoute = pathname?.includes("/playable");
101
+ const isReady = isOrientationReady || isPlayableRoute;
97
102
 
98
103
  React.useEffect(() => {
99
104
  if (ref.current && isActive && isReady) {
@@ -1,35 +1,19 @@
1
1
  import { resolveNavigationPlugin } from "@applicaster/zapp-react-native-utils/navigationUtils";
2
2
 
3
- /**
4
- * This function helps to decide wether the menu should be presented on the screen
5
- * based on route and / or screen Data
6
- *
7
- * is similar to the navbar, except that it covers scenarios where only the navbar (and not)
8
- * the menu, will be hidden
9
- *
10
- * - playable screens
11
- * - qb_search_screen
12
- * - screen hooks that specify showNavBar or presentFullScreen
13
- * - screens or nested screens that have allow_screen_plugin_presentation set to true
14
- *
15
- * @param {String} route current route of the screen
16
- * @param {Object} screenData payload associated with the currently presented screen
17
- * @returns {Boolean}
18
- */
19
- export function isMenuVisible(route, screenData, plugins) {
20
- const plugin = resolveNavigationPlugin({
3
+ export const BOTTOM_TABS_PLUGIN_ID = "quick-brick-bottom-tabs";
4
+
5
+ export const SIDE_MENU_PLUGIN_ID = "quick_brick_side_menu";
6
+
7
+ export function getMenuPlugin(screenData, plugins) {
8
+ return resolveNavigationPlugin({
21
9
  category: "menu",
22
10
  navigations:
23
11
  screenData?.navigations || screenData?.targetScreen?.navigations,
24
12
  plugins,
25
13
  });
14
+ }
26
15
 
27
- const isBottomTabsPlugin = plugin?.identifier === "quick-brick-bottom-tabs";
28
-
29
- if (!isBottomTabsPlugin) {
30
- return false;
31
- }
32
-
16
+ function shouldShowNavigation(route, screenData) {
33
17
  if (route.includes("playable")) {
34
18
  return false;
35
19
  }
@@ -56,3 +40,44 @@ export function isMenuVisible(route, screenData, plugins) {
56
40
 
57
41
  return true;
58
42
  }
43
+
44
+ /**
45
+ * This function helps to decide whether the menu should be presented on the screen
46
+ * based on route and / or screen Data
47
+ *
48
+ * is similar to the navbar, except that it covers scenarios where only the navbar (and not)
49
+ * the menu will be hidden
50
+ *
51
+ * - playable screens
52
+ * - qb_search_screen
53
+ * - screen hooks that specify showNavBar or presentFullScreen
54
+ * - screens or nested screens that have allow_screen_plugin_presentation set to true
55
+ *
56
+ * @param {String} route current route of the screen
57
+ * @param {Object} screenData payload associated with the currently presented screen
58
+ * @returns {Boolean}
59
+ */
60
+ export function isMenuVisible(route, screenData, plugins) {
61
+ const plugin = getMenuPlugin(screenData, plugins);
62
+
63
+ const isBottomTabsPlugin = plugin?.identifier === BOTTOM_TABS_PLUGIN_ID;
64
+ const isSideMenuPlugin = plugin?.identifier === SIDE_MENU_PLUGIN_ID;
65
+
66
+ if (!isBottomTabsPlugin && !isSideMenuPlugin) {
67
+ return false;
68
+ }
69
+
70
+ return shouldShowNavigation(route, screenData);
71
+ }
72
+
73
+ export function isBottomTabVisible(route, screenData, plugins) {
74
+ const plugin = getMenuPlugin(screenData, plugins);
75
+
76
+ const isBottomTabsPlugin = plugin?.identifier === BOTTOM_TABS_PLUGIN_ID;
77
+
78
+ if (!isBottomTabsPlugin) {
79
+ return false;
80
+ }
81
+
82
+ return shouldShowNavigation(route, screenData);
83
+ }
@@ -1,18 +1,16 @@
1
- import React, { useLayoutEffect } from "react";
1
+ import { useLayoutEffect } from "react";
2
2
 
3
3
  import {
4
4
  allowedOrientationsForScreen,
5
- ORIENTATIONS,
6
5
  getOrientation,
6
+ ORIENTATIONS,
7
7
  useGetScreenOrientation,
8
8
  } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
9
9
  import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
10
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
10
+ import { usePlugins, useAppData } from "@applicaster/zapp-react-native-redux";
11
11
  import { findPluginByType } from "@applicaster/zapp-react-native-utils/pluginUtils";
12
12
  import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
13
-
14
- import { ZappHookModalContext } from "../../Contexts";
15
- import { HookModalContextT } from "../../Contexts/ZappHookModalContext";
13
+ import { zappHookModalStore } from "../../Contexts/ZappHookModalContext";
16
14
 
17
15
  /**
18
16
  * This function calls the native module needed to set orientation
@@ -65,8 +63,8 @@ export function useNewOrientationForScreenData({
65
63
  }: OrientationHookArgs) {
66
64
  const isTablet = useIsTablet();
67
65
 
68
- const { appData, plugins } = usePickFromState(["appData", "plugins"]);
69
- const isTabletPortrait = appData?.isTabletPortrait;
66
+ const plugins = usePlugins();
67
+ const { isTabletPortrait } = useAppData();
70
68
  const isLandscapeTablet = isTablet && !isTabletPortrait;
71
69
 
72
70
  const screenOrientation = useGetScreenOrientation(screenData);
@@ -89,10 +87,6 @@ type Props = {
89
87
  };
90
88
 
91
89
  export function useScreenOrientationHandler({ screenData, isActive }: Props) {
92
- const { isHooksExecutionInProgress } = React.useContext<HookModalContextT>(
93
- ZappHookModalContext.ReactContext
94
- );
95
-
96
90
  const prevIsActive = usePrevious(isActive);
97
91
 
98
92
  const newOrientation = useNewOrientationForScreenData({
@@ -105,6 +99,9 @@ export function useScreenOrientationHandler({ screenData, isActive }: Props) {
105
99
  return;
106
100
  }
107
101
 
102
+ // TODO: make sure it can be static getter and subscription is not needed
103
+ const { isHooksExecutionInProgress } = zappHookModalStore.getState();
104
+
108
105
  // If modal hook presented we need to skip
109
106
  // Change orientation for presenter screen
110
107
  if (isHooksExecutionInProgress) {
@@ -116,7 +113,7 @@ export function useScreenOrientationHandler({ screenData, isActive }: Props) {
116
113
 
117
114
  setOrientation(newOrientation);
118
115
  }
119
- }, [newOrientation, isHooksExecutionInProgress, prevIsActive, isActive]);
116
+ }, [newOrientation, prevIsActive, isActive]);
120
117
  }
121
118
 
122
119
  export function useOrientationHandler({ screenData }: OrientationHookArgs) {