@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5114122134 → 12.0.0-alpha.5208030571
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.
- package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
- package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
- package/Components/AudioPlayer/helpers.tsx +1 -0
- package/Components/BaseFocusable/index.tsx +1 -1
- package/Components/Cell/Cell.tsx +7 -3
- package/Components/Cell/CellWithFocusable.tsx +38 -19
- package/Components/Cell/TvOSCellComponent.tsx +2 -2
- package/Components/ErrorScreen/index.tsx +1 -1
- package/Components/FocusableCell/index.tsx +7 -7
- package/Components/FocusableGroup/index.tsx +1 -0
- package/Components/FocusableList/FocusableChild.ts +24 -14
- package/Components/FocusableList/FocusableItem.tsx +1 -1
- package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
- package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
- package/Components/FocusableList/index.tsx +26 -25
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +11 -8
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
- package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
- package/Components/HandlePlayable/HandlePlayable.tsx +4 -1
- package/Components/HookRenderer/index.tsx +1 -6
- package/Components/Image/Image.android.tsx +3 -25
- package/Components/Image/Image.tsx +18 -2
- package/Components/Image/utils/index.ts +39 -0
- package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
- package/Components/Layout/TV/__tests__/index.test.tsx +1 -34
- package/Components/Layout/TV/index.tsx +141 -58
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +0 -1
- package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
- package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +8 -10
- package/Components/MasterCell/utils/index.ts +16 -5
- package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
- package/Components/ModalComponent/Button/Item.tsx +82 -0
- package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
- package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
- package/Components/ModalComponent/Button/assets.ts +4 -0
- package/Components/ModalComponent/Button/index.tsx +111 -0
- package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
- package/Components/ModalComponent/Header/Title.tsx +70 -0
- package/Components/ModalComponent/Header/assets.ts +4 -0
- package/Components/ModalComponent/Header/index.tsx +71 -0
- package/Components/ModalComponent/Header/utils.ts +79 -0
- package/Components/ModalComponent/index.tsx +38 -0
- package/Components/ModalComponent/utils.ts +126 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +63 -31
- package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
- package/Components/PlayerImageBackground/index.tsx +34 -8
- package/Components/River/ComponentsMap/ComponentsMap.tsx +90 -2
- package/Components/River/TV/River.tsx +2 -3
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
- package/Components/River/__tests__/componentsMap.test.js +7 -0
- package/Components/River/useScreenConfiguration.ts +2 -2
- package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
- package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
- package/Components/RouteManager/index.tsx +5 -3
- package/Components/Screen/TV/index.web.tsx +151 -0
- package/Components/Screen/__tests__/Screen.test.tsx +88 -74
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +24 -6
- package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
- package/Components/Screen/index.tsx +103 -43
- package/Components/Screen/navigationHandler.ts +0 -82
- package/Components/ScreenResolver/index.tsx +4 -2
- package/Components/Tabs/TV/styles.ts +1 -1
- package/Components/TrackedView/index.tsx +6 -3
- package/Components/VideoLive/LiveImageManager.ts +37 -10
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +81 -72
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +18 -28
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +342 -0
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +151 -0
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +438 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +143 -0
- package/Components/VideoModal/ModalAnimation/index.ts +15 -0
- package/Components/VideoModal/ModalAnimation/utils.ts +299 -0
- package/Components/VideoModal/PlayerDetails.tsx +3 -0
- package/Components/VideoModal/PlayerWrapper.tsx +65 -42
- package/Components/VideoModal/VideoModal.tsx +8 -25
- package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +0 -2
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +5 -1
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +246 -166
- package/Components/VideoModal/utils.ts +3 -3
- package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
- package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +1 -1
- package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
- package/Components/Viewport/ViewportTracker/index.tsx +1 -0
- package/Components/Viewport/utils.js +1 -1
- package/Components/ZappUIComponent/index.tsx +22 -12
- package/Components/index.js +2 -0
- package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
- package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
- package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
- package/Contexts/ScreenContext/index.tsx +95 -29
- package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
- package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
- package/package.json +5 -5
- package/Components/VideoModal/hooks/usePosition.ts +0 -50
- /package/Contexts/{index.js → index.ts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
-
import { View,
|
|
3
|
+
import { View, StyleSheet, Platform, Animated, FlatList } from "react-native";
|
|
4
4
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
5
5
|
import { RiverItem } from "../RiverItem";
|
|
6
6
|
import { RiverFooter } from "../RiverFooter";
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
import { useScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenData";
|
|
22
22
|
import { isLast } from "@applicaster/zapp-react-native-utils/arrayUtils";
|
|
23
23
|
import { withComponentsMapProvider } from "@applicaster/zapp-react-native-ui-components/Decorators/ComponentsMapWrapper";
|
|
24
|
+
import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
25
|
+
import { useShallow } from "zustand/react/shallow";
|
|
24
26
|
|
|
25
27
|
type Props = {
|
|
26
28
|
feed: ZappFeed;
|
|
@@ -40,8 +42,10 @@ const styles = StyleSheet.create({
|
|
|
40
42
|
},
|
|
41
43
|
});
|
|
42
44
|
|
|
43
|
-
const
|
|
45
|
+
const getNearestValue = (value, optionA, optionB) =>
|
|
46
|
+
Math.abs(value - optionA) < Math.abs(value - optionB) ? optionA : optionB;
|
|
44
47
|
|
|
48
|
+
const scrollIndicatorInsets = { right: 1 };
|
|
45
49
|
const keyExtractor = R.compose(String, R.prop("id"));
|
|
46
50
|
|
|
47
51
|
function ComponentsMapComponent(props: Props) {
|
|
@@ -59,6 +63,7 @@ function ComponentsMapComponent(props: Props) {
|
|
|
59
63
|
refreshingPipesV1,
|
|
60
64
|
} = props;
|
|
61
65
|
|
|
66
|
+
const flatListRef = React.useRef<FlatList | null>(null);
|
|
62
67
|
const screenConfig = useScreenConfiguration(riverId);
|
|
63
68
|
const screenData = useScreenData(riverId);
|
|
64
69
|
const pullToRefreshEnabled = screenData?.rules?.pull_to_refresh_enabled;
|
|
@@ -193,6 +198,68 @@ function ComponentsMapComponent(props: Props) {
|
|
|
193
198
|
[]
|
|
194
199
|
);
|
|
195
200
|
|
|
201
|
+
const navBarStore = useScreenContextV2()._navBarStore;
|
|
202
|
+
|
|
203
|
+
const scrollY = navBarStore(
|
|
204
|
+
useShallow(({ scrollYAnimated }) => scrollYAnimated)
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const canMomentum = React.useRef(false);
|
|
208
|
+
|
|
209
|
+
const onScrollBeginDrag = React.useCallback(() => {
|
|
210
|
+
canMomentum.current = true;
|
|
211
|
+
}, []);
|
|
212
|
+
|
|
213
|
+
/* onMomentumScrollEnd: Handling snap to expanded/collapsed state ( for navbar ) */
|
|
214
|
+
const _onMomentumScrollEnd = React.useCallback(({ nativeEvent }) => {
|
|
215
|
+
if (!canMomentum.current) return;
|
|
216
|
+
canMomentum.current = false;
|
|
217
|
+
|
|
218
|
+
const { height: headerHeight, scrollState } = navBarStore.getState();
|
|
219
|
+
|
|
220
|
+
const offsetY = nativeEvent.contentOffset.y;
|
|
221
|
+
|
|
222
|
+
if (
|
|
223
|
+
!(scrollState === 0 || scrollState === -headerHeight) &&
|
|
224
|
+
flatListRef.current
|
|
225
|
+
) {
|
|
226
|
+
const offset =
|
|
227
|
+
getNearestValue(Math.round(scrollState), -headerHeight, 0) ===
|
|
228
|
+
-headerHeight
|
|
229
|
+
? Math.round(offsetY + scrollState + headerHeight)
|
|
230
|
+
: Math.round(offsetY + scrollState);
|
|
231
|
+
|
|
232
|
+
flatListRef.current.scrollToOffset({
|
|
233
|
+
animated: true,
|
|
234
|
+
offset,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}, []);
|
|
238
|
+
|
|
239
|
+
/* onScrollEnd: Handling snap to expanded/collapsed state ( for navbar ) */
|
|
240
|
+
const _onScrollEndDrag = React.useCallback(({ nativeEvent }) => {
|
|
241
|
+
const { height: headerHeight, scrollState } = navBarStore.getState();
|
|
242
|
+
|
|
243
|
+
const offsetY = nativeEvent.contentOffset.y;
|
|
244
|
+
|
|
245
|
+
if (
|
|
246
|
+
!(scrollState === 0 || scrollState === -headerHeight) &&
|
|
247
|
+
flatListRef.current &&
|
|
248
|
+
nativeEvent.velocity.y === 0
|
|
249
|
+
) {
|
|
250
|
+
const offset =
|
|
251
|
+
getNearestValue(Math.round(scrollState), -headerHeight, 0) ===
|
|
252
|
+
-headerHeight
|
|
253
|
+
? Math.round(offsetY + scrollState + headerHeight)
|
|
254
|
+
: Math.round(offsetY + scrollState);
|
|
255
|
+
|
|
256
|
+
flatListRef.current.scrollToOffset({
|
|
257
|
+
animated: true,
|
|
258
|
+
offset,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}, []);
|
|
262
|
+
|
|
196
263
|
if (shouldShowLoadingError) {
|
|
197
264
|
return <RiverError />;
|
|
198
265
|
}
|
|
@@ -208,6 +275,9 @@ function ComponentsMapComponent(props: Props) {
|
|
|
208
275
|
>
|
|
209
276
|
<ViewportTracker>
|
|
210
277
|
<FlatList
|
|
278
|
+
ref={(ref) => {
|
|
279
|
+
flatListRef.current = ref;
|
|
280
|
+
}}
|
|
211
281
|
// Fix for WebView rerender crashes on Android API 28+
|
|
212
282
|
// https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
|
|
213
283
|
overScrollMode={Platform.OS === "android" ? "never" : "auto"}
|
|
@@ -227,6 +297,24 @@ function ComponentsMapComponent(props: Props) {
|
|
|
227
297
|
}
|
|
228
298
|
ListFooterComponent={renderFooter}
|
|
229
299
|
refreshControl={refreshControl}
|
|
300
|
+
onScrollBeginDrag={onScrollBeginDrag}
|
|
301
|
+
onScroll={Animated.event(
|
|
302
|
+
[
|
|
303
|
+
{
|
|
304
|
+
nativeEvent: {
|
|
305
|
+
contentOffset: {
|
|
306
|
+
y: scrollY,
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
{
|
|
312
|
+
useNativeDriver: false,
|
|
313
|
+
}
|
|
314
|
+
)}
|
|
315
|
+
onMomentumScrollEnd={_onMomentumScrollEnd}
|
|
316
|
+
onScrollEndDrag={_onScrollEndDrag}
|
|
317
|
+
scrollEventThrottle={16}
|
|
230
318
|
{...scrollViewExtraProps}
|
|
231
319
|
/>
|
|
232
320
|
</ViewportTracker>
|
|
@@ -4,7 +4,6 @@ import * as React from "react";
|
|
|
4
4
|
import { Text } from "react-native";
|
|
5
5
|
import * as R from "ramda";
|
|
6
6
|
|
|
7
|
-
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
8
7
|
import {
|
|
9
8
|
useFeedLoader,
|
|
10
9
|
useLayoutVersion,
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
useNavbarState,
|
|
21
20
|
useSetNavbarState,
|
|
22
21
|
} from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
22
|
+
import { useRivers } from "@applicaster/zapp-react-native-utils/reactHooks/state";
|
|
23
23
|
|
|
24
24
|
type Props = {
|
|
25
25
|
screenId: string;
|
|
@@ -53,7 +53,7 @@ export const River = (props: Props) => {
|
|
|
53
53
|
|
|
54
54
|
const navigationPluginData = useNavigationPluginData();
|
|
55
55
|
|
|
56
|
-
const
|
|
56
|
+
const rivers = useRivers();
|
|
57
57
|
const isV2 = useLayoutVersion({ isV2: true });
|
|
58
58
|
|
|
59
59
|
const river = React.useMemo(() => rivers?.[screenId], [screenId]);
|
|
@@ -112,7 +112,6 @@ export const River = (props: Props) => {
|
|
|
112
112
|
|
|
113
113
|
focusManager.setupInitialFocus(groupId, true);
|
|
114
114
|
focusManager.setInitialFocus();
|
|
115
|
-
focusManager.enableAutomaticFocus();
|
|
116
115
|
}
|
|
117
116
|
}, [river, componentsMapExtraProps]);
|
|
118
117
|
|
|
@@ -64,7 +64,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
64
64
|
refreshControl={null}
|
|
65
65
|
removeClippedSubviews={true}
|
|
66
66
|
renderItem={[Function]}
|
|
67
|
-
scrollEventThrottle={
|
|
67
|
+
scrollEventThrottle={16}
|
|
68
68
|
scrollIndicatorInsets={
|
|
69
69
|
Object {
|
|
70
70
|
"right": 1,
|
|
@@ -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", () => ({
|
|
@@ -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
|
|
49
|
+
const rivers = useRivers();
|
|
50
50
|
const isTablet = useIsTablet();
|
|
51
51
|
|
|
52
52
|
return useMemo(() => {
|
|
@@ -2,11 +2,17 @@ import React from "react";
|
|
|
2
2
|
import { shallow } from "enzyme";
|
|
3
3
|
import { shallowToJson } from "enzyme-to-json";
|
|
4
4
|
|
|
5
|
-
import { ScreenResolver } from "@applicaster/zapp-react-native-ui-components/Components/ScreenResolver";
|
|
6
|
-
|
|
7
5
|
const pathname = "/river/A1234";
|
|
8
6
|
const screenData = { id: "A1234" };
|
|
9
7
|
|
|
8
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
9
|
+
useRoute: jest.fn(() => ({ pathname: pathname, screenData: screenData })),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
ScreenResolver,
|
|
14
|
+
} = require("@applicaster/zapp-react-native-ui-components/Components/ScreenResolver");
|
|
15
|
+
|
|
10
16
|
const { RouteManager } = require("../index");
|
|
11
17
|
|
|
12
18
|
describe("<RouteManager />", () => {
|
|
@@ -20,6 +26,7 @@ describe("<RouteManager />", () => {
|
|
|
20
26
|
|
|
21
27
|
it("passes the correct props to ScreenResolver", () => {
|
|
22
28
|
expect(wrapper.find(ScreenResolver).props()).toEqual({
|
|
29
|
+
pathname: "/river/A1234",
|
|
23
30
|
screenType: "river",
|
|
24
31
|
screenId: "A1234",
|
|
25
32
|
screenData: { id: "A1234" },
|
|
@@ -2,6 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
|
|
4
4
|
import { getPathAttributes } from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
5
|
+
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
5
6
|
import { ScreenResolver } from "../ScreenResolver";
|
|
6
7
|
import { TestId } from "./TestId";
|
|
7
8
|
|
|
@@ -12,7 +13,8 @@ type Props = { pathname: string; screenData: Record<string, any> };
|
|
|
12
13
|
* and reconciles data returned from hook plugins if needed
|
|
13
14
|
* @param {Props}: see props above
|
|
14
15
|
*/
|
|
15
|
-
export function RouteManager(
|
|
16
|
+
export function RouteManager(props: Props) {
|
|
17
|
+
const { pathname, screenData: _screenData } = useRoute();
|
|
16
18
|
const pathAttributes = getPathAttributes({ pathname });
|
|
17
19
|
|
|
18
20
|
const { screenType, screenId } = R.last(pathAttributes);
|
|
@@ -22,8 +24,8 @@ export function RouteManager({ pathname, screenData, ...otherProps }: Props) {
|
|
|
22
24
|
<ScreenResolver
|
|
23
25
|
screenType={screenType}
|
|
24
26
|
screenId={screenId}
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
+
{...props}
|
|
28
|
+
screenData={props.screenData}
|
|
27
29
|
/>
|
|
28
30
|
</TestId>
|
|
29
31
|
);
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { last, pathOr } from "ramda";
|
|
3
|
+
import { ScreenResolver } from "../../ScreenResolver";
|
|
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 { NavBarContainer } from "../../Layout/TV/NavBarContainer";
|
|
11
|
+
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
12
|
+
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
13
|
+
|
|
14
|
+
type Components = {
|
|
15
|
+
NavBar: React.ComponentType<any>;
|
|
16
|
+
Background: React.ComponentType<any>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type Props = {
|
|
20
|
+
route: string;
|
|
21
|
+
Components: Components;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const isPlayer = (routeState): boolean => {
|
|
25
|
+
const pluginType = pathOr(
|
|
26
|
+
"unknown_plugin_type",
|
|
27
|
+
["state", "screen", "plugin_type"],
|
|
28
|
+
routeState
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return pluginType === "player";
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const getActiveRiver = ({
|
|
35
|
+
screenId,
|
|
36
|
+
screenType,
|
|
37
|
+
routeState,
|
|
38
|
+
rivers,
|
|
39
|
+
fallbackActiveRiver,
|
|
40
|
+
}) => {
|
|
41
|
+
if (rivers[screenId]) {
|
|
42
|
+
// river presents
|
|
43
|
+
return screenType === "hooks"
|
|
44
|
+
? routeState?.state?.entry?.payload
|
|
45
|
+
: routeState?.state?.screen;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// screenId is not presented into rivers, use fallback instead
|
|
49
|
+
return fallbackActiveRiver;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const getNavigations = ({
|
|
53
|
+
screenId,
|
|
54
|
+
screenType,
|
|
55
|
+
routeState,
|
|
56
|
+
rivers,
|
|
57
|
+
fallbackNavigations,
|
|
58
|
+
}) => {
|
|
59
|
+
if (rivers[screenId]) {
|
|
60
|
+
// river presents
|
|
61
|
+
return screenType === "hooks"
|
|
62
|
+
? routeState?.state?.entry?.payload.navigations
|
|
63
|
+
: routeState?.state?.screen?.navigations;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// screenId is not presented into rivers, use fallback instead
|
|
67
|
+
return fallbackNavigations;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const Screen = (props: Props) => {
|
|
71
|
+
if (!props.route) {
|
|
72
|
+
throw new Error("Required props route is missing");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const navigator = useNavigation();
|
|
76
|
+
const { plugins = [], rivers = [] } = usePickFromState(["plugins", "rivers"]);
|
|
77
|
+
|
|
78
|
+
const pathAttributes = getPathAttributes({ pathname: props.route });
|
|
79
|
+
const routeState = navigator.getStackForPathname(props.route);
|
|
80
|
+
|
|
81
|
+
const { screenType, screenId } = last(pathAttributes);
|
|
82
|
+
|
|
83
|
+
// console.log("debug_2", "Screen", {
|
|
84
|
+
// screenType,
|
|
85
|
+
// routeState,
|
|
86
|
+
// route: props.route,
|
|
87
|
+
// navigator,
|
|
88
|
+
// });
|
|
89
|
+
|
|
90
|
+
const activeRiver = getActiveRiver({
|
|
91
|
+
screenId,
|
|
92
|
+
screenType,
|
|
93
|
+
routeState,
|
|
94
|
+
rivers,
|
|
95
|
+
fallbackActiveRiver: navigator.activeRiver,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const navigationProps = getNavigationProps({
|
|
99
|
+
navigator: {
|
|
100
|
+
// @ts-ignore
|
|
101
|
+
activeRiver,
|
|
102
|
+
// @ts-ignore
|
|
103
|
+
screenTitle: routeState.state.entry?.title || routeState.screen?.name,
|
|
104
|
+
},
|
|
105
|
+
category: "menu",
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const navigations = getNavigations({
|
|
109
|
+
screenId,
|
|
110
|
+
screenType,
|
|
111
|
+
routeState,
|
|
112
|
+
rivers,
|
|
113
|
+
fallbackNavigations: navigator.activeRiver.navigations,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const menuPlugin = resolveNavigationPlugin({
|
|
117
|
+
category: "menu",
|
|
118
|
+
navigations,
|
|
119
|
+
plugins,
|
|
120
|
+
}) as QuickBrickPlugin;
|
|
121
|
+
|
|
122
|
+
const NavBar = (props.Components.NavBar ||
|
|
123
|
+
menuPlugin.module) as React.ComponentType<any>;
|
|
124
|
+
|
|
125
|
+
const isNavBarVisible = !isPlayer(routeState);
|
|
126
|
+
|
|
127
|
+
const screenData =
|
|
128
|
+
screenType === "hooks"
|
|
129
|
+
? routeState?.state?.entry
|
|
130
|
+
: routeState?.state?.screen;
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<>
|
|
134
|
+
<NavBarContainer isVisible={isNavBarVisible} onReady={noop}>
|
|
135
|
+
<NavBar
|
|
136
|
+
groupId={props.route}
|
|
137
|
+
isVisible={isNavBarVisible}
|
|
138
|
+
menuPlugin={menuPlugin}
|
|
139
|
+
navigationProps={navigationProps}
|
|
140
|
+
// Maybe activeRiver?
|
|
141
|
+
/>
|
|
142
|
+
</NavBarContainer>
|
|
143
|
+
<ScreenResolver
|
|
144
|
+
screenType={screenType}
|
|
145
|
+
screenId={screenId}
|
|
146
|
+
screenData={screenData}
|
|
147
|
+
groupId={props.route}
|
|
148
|
+
/>
|
|
149
|
+
</>
|
|
150
|
+
);
|
|
151
|
+
};
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { View } from "react-native";
|
|
3
|
-
import {
|
|
3
|
+
import { act, create } from "react-test-renderer";
|
|
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
|
+
jest.mock("react-native-safe-area-context", () => ({
|
|
14
|
+
useSafeAreaInsets: () => ({ top: 44 }),
|
|
15
|
+
}));
|
|
16
|
+
|
|
9
17
|
jest.mock("../../RouteManager", () => ({
|
|
10
18
|
RouteManager: Mocked_RouteManager,
|
|
11
19
|
}));
|
|
@@ -23,59 +31,54 @@ jest.mock(
|
|
|
23
31
|
})
|
|
24
32
|
);
|
|
25
33
|
|
|
26
|
-
let mock_navBarVisibleFlag = true;
|
|
27
|
-
|
|
28
|
-
jest.mock("../navigationHandler.ts", () => ({
|
|
29
|
-
isNavBarVisible: jest.fn(() => mock_navBarVisibleFlag),
|
|
30
|
-
}));
|
|
31
|
-
|
|
32
34
|
jest.mock("@applicaster/zapp-react-native-utils/analyticsUtils", () => ({
|
|
33
35
|
useAnalytics: jest.fn(() => ({
|
|
34
36
|
sendScreenEvent: jest.fn(),
|
|
35
37
|
})),
|
|
36
38
|
}));
|
|
37
39
|
|
|
38
|
-
const {
|
|
39
|
-
allowedOrientationsForScreen,
|
|
40
|
-
getOrientation,
|
|
41
|
-
useGetScreenOrientation,
|
|
42
|
-
} = require("@applicaster/zapp-react-native-utils/appUtils/orientationHelper");
|
|
43
|
-
|
|
44
40
|
jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
|
|
45
41
|
useTheme: jest.fn(() => ({
|
|
46
42
|
app_background_color: "blue",
|
|
47
43
|
})),
|
|
48
44
|
}));
|
|
49
45
|
|
|
46
|
+
const NavBar = jest.fn((props) => <View testID="navBar" {...props} />);
|
|
47
|
+
|
|
48
|
+
jest.mock(
|
|
49
|
+
"@applicaster/zapp-react-native-app/App/Layout/layoutHelpers",
|
|
50
|
+
() => ({
|
|
51
|
+
getNavigationPluginModule: () => NavBar,
|
|
52
|
+
})
|
|
53
|
+
);
|
|
54
|
+
|
|
50
55
|
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
|
|
51
|
-
|
|
56
|
+
...(jest.requireActual(
|
|
57
|
+
"@applicaster/zapp-react-native-utils/reactHooks/screen"
|
|
58
|
+
) as any),
|
|
59
|
+
useScreenData: jest.fn(() => ({
|
|
52
60
|
id: "testId",
|
|
61
|
+
navigations: [{ id: "testId", category: "nav_bar" }],
|
|
53
62
|
})),
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
useCurrentScreenData: jest.fn(() => ({
|
|
64
|
+
id: "testId",
|
|
56
65
|
})),
|
|
57
66
|
}));
|
|
58
67
|
|
|
59
|
-
const {
|
|
60
|
-
useCurrentScreenData,
|
|
61
|
-
} = require("@applicaster/zapp-react-native-utils/reactHooks/screen");
|
|
62
|
-
|
|
63
|
-
const { isNavBarVisible } = require("../navigationHandler");
|
|
64
|
-
|
|
65
|
-
const NavBar = jest.fn((props) => <View testID="navBar" {...props} />);
|
|
66
|
-
const navigator = {};
|
|
67
|
-
const navBarProps = { nav_items: [] };
|
|
68
|
-
const screenData = {};
|
|
69
|
-
|
|
70
68
|
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
69
|
+
isNavBarVisible: mockIsNavBarVisible,
|
|
71
70
|
useRoute: jest.fn(() => ({
|
|
72
71
|
pathname: "/river/testId",
|
|
72
|
+
screenData: { id: "testId" },
|
|
73
73
|
})),
|
|
74
74
|
useNavigation: jest.fn(() => ({
|
|
75
75
|
canGoBack: () => false,
|
|
76
76
|
currentRoute: "/river/testId",
|
|
77
|
+
activeRiver: { id: "testId" },
|
|
78
|
+
screenData: { id: "testId" },
|
|
77
79
|
data: { screen: { id: "testId" } },
|
|
78
80
|
})),
|
|
81
|
+
useIsScreenActive: jest.fn().mockReturnValue(true),
|
|
79
82
|
}));
|
|
80
83
|
|
|
81
84
|
jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
|
|
@@ -93,9 +96,26 @@ jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
|
|
|
93
96
|
}),
|
|
94
97
|
}));
|
|
95
98
|
|
|
99
|
+
const {
|
|
100
|
+
ORIENTATIONS,
|
|
101
|
+
} = require("@applicaster/zapp-react-native-utils/appUtils/orientationHelper");
|
|
102
|
+
|
|
103
|
+
const {
|
|
104
|
+
useCurrentScreenData,
|
|
105
|
+
} = require("@applicaster/zapp-react-native-utils/reactHooks/screen");
|
|
106
|
+
|
|
107
|
+
const {
|
|
108
|
+
allowedOrientationsForScreen,
|
|
109
|
+
getOrientation,
|
|
110
|
+
useGetScreenOrientation,
|
|
111
|
+
} = require("@applicaster/zapp-react-native-utils/appUtils/orientationHelper");
|
|
112
|
+
|
|
113
|
+
const navigator = {};
|
|
114
|
+
const navBarProps = { nav_items: [] };
|
|
115
|
+
const screenData = {};
|
|
116
|
+
|
|
96
117
|
const screenProps = {
|
|
97
118
|
pathname: "/foo/path",
|
|
98
|
-
NavBar,
|
|
99
119
|
navigator,
|
|
100
120
|
navBarProps,
|
|
101
121
|
screenData,
|
|
@@ -109,7 +129,6 @@ describe("<Screen Component />", () => {
|
|
|
109
129
|
beforeEach(() => {
|
|
110
130
|
allowedOrientationsForScreen.mockClear();
|
|
111
131
|
getOrientation.mockClear();
|
|
112
|
-
isNavBarVisible.mockClear();
|
|
113
132
|
useGetScreenOrientation.mockClear();
|
|
114
133
|
});
|
|
115
134
|
|
|
@@ -125,14 +144,9 @@ describe("<Screen Component />", () => {
|
|
|
125
144
|
|
|
126
145
|
describe("when the navbar should be hidden", () => {
|
|
127
146
|
beforeEach(() => {
|
|
128
|
-
mock_navBarVisibleFlag = false;
|
|
129
147
|
wrapper = create(<Screen {...screenProps} />);
|
|
130
148
|
});
|
|
131
149
|
|
|
132
|
-
afterEach(() => {
|
|
133
|
-
mock_navBarVisibleFlag = true;
|
|
134
|
-
});
|
|
135
|
-
|
|
136
150
|
it("renders correctly", () => {
|
|
137
151
|
act(() => {
|
|
138
152
|
wrapper = create(<Screen {...screenProps} />);
|
|
@@ -142,44 +156,44 @@ describe("<Screen Component />", () => {
|
|
|
142
156
|
});
|
|
143
157
|
});
|
|
144
158
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
159
|
+
it("invokes the orientation hook when the route changes", () => {
|
|
160
|
+
act(() => {
|
|
161
|
+
wrapper = create(<Screen {...screenProps} />);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// @ts-ignore
|
|
165
|
+
useCurrentScreenData.mockReturnValue({
|
|
166
|
+
id: "player-testId",
|
|
167
|
+
general: { screen_orientation: ORIENTATIONS.landscapeSensor },
|
|
168
|
+
plugin_type: "player",
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
act(() => {
|
|
172
|
+
wrapper.update(
|
|
173
|
+
<Screen
|
|
174
|
+
{...screenProps}
|
|
175
|
+
pathname="/playable"
|
|
176
|
+
screenData={{
|
|
177
|
+
id: "player-testId",
|
|
178
|
+
general: { screen_orientation: ORIENTATIONS.landscapeSensor },
|
|
179
|
+
plugin_type: "player",
|
|
180
|
+
}}
|
|
181
|
+
/>
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
useCurrentScreenData.mockReturnValue({
|
|
186
|
+
id: "player-testId",
|
|
187
|
+
general: { screen_orientation: ORIENTATIONS.landscapeSensor },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
act(() => {
|
|
191
|
+
wrapper.update(<Screen {...screenProps} />);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(allowedOrientationsForScreen).toHaveBeenNthCalledWith(
|
|
195
|
+
1,
|
|
196
|
+
ORIENTATIONS.landscapeSensor
|
|
197
|
+
);
|
|
198
|
+
});
|
|
185
199
|
});
|