@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.
- package/Components/BaseFocusable/index.tsx +3 -3
- package/Components/Cell/Cell.tsx +7 -7
- package/Components/Cell/CellWithFocusable.tsx +40 -19
- package/Components/Cell/TvOSCellComponent.tsx +5 -9
- package/Components/ErrorScreen/index.tsx +1 -1
- package/Components/Focusable/Focusable.tsx +6 -1
- package/Components/FocusableCell/index.tsx +7 -7
- package/Components/FocusableGroup/index.tsx +11 -2
- 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 +6 -3
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
- package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
- package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
- 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__/__snapshots__/index.test.tsx.snap +10 -10
- package/Components/Layout/TV/__tests__/index.test.tsx +19 -75
- package/Components/Layout/TV/index.tsx +12 -11
- package/Components/Layout/TV/index.web.tsx +33 -0
- package/Components/Layout/index.tsx +15 -11
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -112
- package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
- 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/ImageContainer/index.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
- package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
- package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
- package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
- package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
- package/Components/MasterCell/utils/index.ts +33 -35
- package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
- package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
- package/Components/Navigator/StackNavigator.tsx +54 -0
- package/Components/Navigator/index.ts +1 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +69 -31
- package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
- package/Components/PlayerImageBackground/index.tsx +34 -8
- package/Components/River/ComponentsMap/ComponentsMap.tsx +92 -22
- package/Components/River/RefreshControl.tsx +55 -1
- package/Components/River/TV/River.tsx +2 -25
- package/Components/River/__tests__/componentsMap.test.js +7 -0
- package/Components/River/useScreenConfiguration.ts +2 -2
- package/Components/Screen/TV/hooks/index.ts +3 -0
- package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
- package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
- package/Components/Screen/TV/index.web.tsx +186 -0
- package/Components/Screen/__tests__/Screen.test.tsx +8 -1
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +14 -0
- package/Components/Screen/index.tsx +78 -30
- package/Components/ScreenResolver/index.tsx +3 -1
- package/Components/Tabs/TV/styles.ts +1 -1
- package/Components/Transitioner/Scene.tsx +24 -19
- package/Components/Transitioner/Transitioner.tsx +21 -28
- package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +6 -2
- package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
- package/Components/VideoLive/LiveImageManager.ts +28 -1
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -28
- 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 +352 -0
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +158 -0
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +455 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +162 -0
- package/Components/VideoModal/ModalAnimation/index.ts +15 -0
- package/Components/VideoModal/ModalAnimation/utils.ts +301 -0
- package/Components/VideoModal/PlayerDetails.tsx +3 -3
- package/Components/VideoModal/PlayerWrapper.tsx +77 -47
- package/Components/VideoModal/VideoModal.tsx +23 -40
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -1
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +157 -105
- package/Components/VideoModal/utils.ts +8 -5
- package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
- package/Components/Viewport/ViewportTracker/index.tsx +1 -0
- package/Components/Viewport/utils.js +1 -1
- package/Components/ZappUIComponent/index.tsx +22 -12
- package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
- package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
- package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
- package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
- package/Contexts/FocusableGroupContext/index.ts +7 -0
- package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -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 +94 -28
- package/Contexts/ScreenDataContext/index.tsx +20 -0
- package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
- package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
- package/events/index.ts +1 -0
- package/package.json +5 -5
- package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
- package/Components/VideoModal/hooks/usePosition.ts +0 -50
- /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
- /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
- /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
|
|
49
|
+
const rivers = useRivers();
|
|
50
50
|
const isTablet = useIsTablet();
|
|
51
51
|
|
|
52
52
|
return useMemo(() => {
|
|
@@ -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 {
|
|
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 {
|
|
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:
|
|
62
|
+
pathname: unknown;
|
|
44
63
|
screenData: unknown;
|
|
45
|
-
setToggleMenu: () => void;
|
|
46
|
-
setCloseMenu: () => void;
|
|
47
64
|
};
|
|
48
65
|
|
|
49
|
-
export function Screen(
|
|
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 {
|
|
76
|
+
const {
|
|
77
|
+
width: screenWidth,
|
|
78
|
+
height,
|
|
79
|
+
deviceInfo,
|
|
80
|
+
} = useDimensions("screen", {
|
|
59
81
|
fullDimensions: true,
|
|
60
82
|
});
|
|
61
83
|
|
|
62
|
-
const
|
|
84
|
+
const { screenData, pathname } = useRoute();
|
|
63
85
|
|
|
64
86
|
const currentRiver = useScreenData(
|
|
65
|
-
(
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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={
|
|
132
|
-
{/* RouteManager
|
|
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
|
}
|
|
@@ -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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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,
|
|
66
|
+
style={[style, contentStyle, dimensions]}
|
|
65
67
|
>
|
|
66
|
-
<
|
|
67
|
-
{
|
|
68
|
-
|
|
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
|
}
|