@applicaster/zapp-react-native-ui-components 13.0.0-alpha.7122212380 → 13.0.0-alpha.7196055925

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.
@@ -17,7 +17,138 @@ exports[`componentsMap renders renders components map correctly 1`] = `
17
17
  }
18
18
  >
19
19
  <RCTScrollView
20
- ListFooterComponent={[Function]}
20
+ ListFooterComponent={
21
+ <Memo(RiverFooterComponent)
22
+ flatListHeight={null}
23
+ loadingState={
24
+ BehaviorSubject {
25
+ "_isScalar": false,
26
+ "_value": {
27
+ "done": false,
28
+ "index": -1,
29
+ "waitForAllComponents": false,
30
+ },
31
+ "closed": false,
32
+ "hasError": false,
33
+ "isStopped": false,
34
+ "observers": [
35
+ Subscriber {
36
+ "_parentOrParents": null,
37
+ "_subscriptions": [
38
+ SubjectSubscription {
39
+ "_parentOrParents": [Circular],
40
+ "_subscriptions": null,
41
+ "closed": false,
42
+ "subject": [Circular],
43
+ "subscriber": [Circular],
44
+ },
45
+ ],
46
+ "closed": false,
47
+ "destination": SafeSubscriber {
48
+ "_complete": undefined,
49
+ "_context": [Circular],
50
+ "_error": undefined,
51
+ "_next": [Function],
52
+ "_parentOrParents": null,
53
+ "_parentSubscriber": [Circular],
54
+ "_subscriptions": null,
55
+ "closed": false,
56
+ "destination": {
57
+ "closed": true,
58
+ "complete": [Function],
59
+ "error": [Function],
60
+ "next": [Function],
61
+ },
62
+ "isStopped": false,
63
+ "syncErrorThrowable": false,
64
+ "syncErrorThrown": false,
65
+ "syncErrorValue": null,
66
+ },
67
+ "isStopped": false,
68
+ "syncErrorThrowable": true,
69
+ "syncErrorThrown": false,
70
+ "syncErrorValue": null,
71
+ },
72
+ Subscriber {
73
+ "_parentOrParents": null,
74
+ "_subscriptions": [
75
+ SubjectSubscription {
76
+ "_parentOrParents": [Circular],
77
+ "_subscriptions": null,
78
+ "closed": false,
79
+ "subject": [Circular],
80
+ "subscriber": [Circular],
81
+ },
82
+ ],
83
+ "closed": false,
84
+ "destination": SafeSubscriber {
85
+ "_complete": undefined,
86
+ "_context": [Circular],
87
+ "_error": undefined,
88
+ "_next": [Function],
89
+ "_parentOrParents": null,
90
+ "_parentSubscriber": [Circular],
91
+ "_subscriptions": null,
92
+ "closed": false,
93
+ "destination": {
94
+ "closed": true,
95
+ "complete": [Function],
96
+ "error": [Function],
97
+ "next": [Function],
98
+ },
99
+ "isStopped": false,
100
+ "syncErrorThrowable": false,
101
+ "syncErrorThrown": false,
102
+ "syncErrorValue": null,
103
+ },
104
+ "isStopped": false,
105
+ "syncErrorThrowable": true,
106
+ "syncErrorThrown": false,
107
+ "syncErrorValue": null,
108
+ },
109
+ Subscriber {
110
+ "_parentOrParents": null,
111
+ "_subscriptions": [
112
+ SubjectSubscription {
113
+ "_parentOrParents": [Circular],
114
+ "_subscriptions": null,
115
+ "closed": false,
116
+ "subject": [Circular],
117
+ "subscriber": [Circular],
118
+ },
119
+ ],
120
+ "closed": false,
121
+ "destination": SafeSubscriber {
122
+ "_complete": undefined,
123
+ "_context": [Circular],
124
+ "_error": undefined,
125
+ "_next": [Function],
126
+ "_parentOrParents": null,
127
+ "_parentSubscriber": [Circular],
128
+ "_subscriptions": null,
129
+ "closed": false,
130
+ "destination": {
131
+ "closed": true,
132
+ "complete": [Function],
133
+ "error": [Function],
134
+ "next": [Function],
135
+ },
136
+ "isStopped": false,
137
+ "syncErrorThrowable": false,
138
+ "syncErrorThrown": false,
139
+ "syncErrorValue": null,
140
+ },
141
+ "isStopped": false,
142
+ "syncErrorThrowable": true,
143
+ "syncErrorThrown": false,
144
+ "syncErrorValue": null,
145
+ },
146
+ ],
147
+ "thrownError": null,
148
+ }
149
+ }
150
+ />
151
+ }
21
152
  contentContainerStyle={
22
153
  {
23
154
  "paddingBottom": undefined,
@@ -80,22 +211,14 @@ exports[`componentsMap renders renders components map correctly 1`] = `
80
211
  style={null}
81
212
  >
82
213
  <View
214
+ onLayout={[Function]}
83
215
  style={
84
216
  {
85
- "display": "flex",
217
+ "flex": 1,
86
218
  }
87
219
  }
88
220
  >
89
- <View
90
- onLayout={[Function]}
91
- style={
92
- {
93
- "flex": 1,
94
- }
95
- }
96
- >
97
- <View />
98
- </View>
221
+ <View />
99
222
  </View>
100
223
  </View>
101
224
  <View
@@ -103,23 +226,13 @@ exports[`componentsMap renders renders components map correctly 1`] = `
103
226
  style={null}
104
227
  >
105
228
  <View
229
+ onLayout={[Function]}
106
230
  style={
107
231
  {
108
- "display": "none",
232
+ "flex": 1,
109
233
  }
110
234
  }
111
- >
112
- <View
113
- onLayout={[Function]}
114
- style={
115
- {
116
- "flex": 1,
117
- }
118
- }
119
- >
120
- <View />
121
- </View>
122
- </View>
235
+ />
123
236
  </View>
124
237
  <View
125
238
  onLayout={[Function]}
@@ -127,12 +240,16 @@ exports[`componentsMap renders renders components map correctly 1`] = `
127
240
  <View
128
241
  renderToHardwareTextureAndroid={true}
129
242
  style={
130
- {
131
- "alignItems": "center",
132
- "height": null,
133
- "justifyContent": "center",
134
- "width": "100%",
135
- }
243
+ [
244
+ {
245
+ "alignItems": "center",
246
+ "justifyContent": "center",
247
+ "width": "100%",
248
+ },
249
+ {
250
+ "height": null,
251
+ },
252
+ ]
136
253
  }
137
254
  >
138
255
  <ActivityIndicator
@@ -1,6 +1,5 @@
1
1
  import React from "react";
2
- import TestRenderer from "react-test-renderer";
3
- import { cleanup } from "@testing-library/react-native";
2
+ import { cleanup, render } from "@testing-library/react-native";
4
3
  import { Provider } from "react-redux";
5
4
  import configureStore from "redux-mock-store";
6
5
 
@@ -183,12 +182,12 @@ describe("componentsMap", () => {
183
182
  .spyOn(themeUtils, "useTheme")
184
183
  .mockImplementation(() => () => theme);
185
184
 
186
- const wrapper = TestRenderer.create(
185
+ const { toJSON } = render(
187
186
  <Provider store={store}>
188
187
  <ComponentsMap {...props} />
189
188
  </Provider>
190
189
  );
191
190
 
192
- expect(wrapper.toJSON()).toMatchSnapshot();
191
+ expect(toJSON()).toMatchSnapshot();
193
192
  });
194
193
  });
@@ -0,0 +1,56 @@
1
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
2
+ import {
3
+ useGetScreenOrientation,
4
+ isOrientationCompatible,
5
+ } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
6
+ import {
7
+ useCurrentScreenData,
8
+ useDimensions,
9
+ useRoute,
10
+ } from "@applicaster/zapp-react-native-utils/reactHooks";
11
+ import { useMemo, useEffect, useState } from "react";
12
+
13
+ export const useWaitForValidOrientation = () => {
14
+ const {
15
+ width: screenWidth,
16
+ height,
17
+ deviceInfo,
18
+ } = useDimensions("screen", {
19
+ fullDimensions: true,
20
+ updateForInactiveScreens: false,
21
+ });
22
+
23
+ const currentScreenData = useCurrentScreenData();
24
+
25
+ const { screenData } = useRoute();
26
+
27
+ const [readyState, setReadyState] = useState(false);
28
+
29
+ const isTablet = deviceInfo?.isTablet;
30
+
31
+ const { appData } = usePickFromState(["appData"]);
32
+ const isTabletPortrait = appData?.isTabletPortrait;
33
+
34
+ const layoutData = useMemo(
35
+ () => ({ isTablet, isTabletPortrait, width: screenWidth, height }),
36
+ [isTablet, isTabletPortrait, screenWidth, height]
37
+ );
38
+
39
+ const targetScreenData =
40
+ currentScreenData || (screenData as any)?.targetScreen || screenData;
41
+
42
+ const orientation = useGetScreenOrientation(targetScreenData);
43
+
44
+ const isReadyForDisplay = isOrientationCompatible({
45
+ orientation,
46
+ layoutData,
47
+ });
48
+
49
+ useEffect(() => {
50
+ if (isReadyForDisplay && !readyState) {
51
+ setReadyState(true);
52
+ }
53
+ }, [readyState, orientation, layoutData]);
54
+
55
+ return readyState;
56
+ };
@@ -2,10 +2,6 @@
2
2
  import React from "react";
3
3
  import { View } from "react-native";
4
4
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
- import {
6
- isOrientationCompatible,
7
- useGetScreenOrientation,
8
- } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
9
5
 
10
6
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
11
7
  import { getComponentModule } from "@applicaster/zapp-react-native-utils/pluginUtils";
@@ -15,7 +11,6 @@ import {
15
11
  getScreenId,
16
12
  } from "@applicaster/zapp-react-native-utils/navigationUtils";
17
13
  import {
18
- useDimensions,
19
14
  useRoute,
20
15
  useCurrentScreenData,
21
16
  useNavbarState,
@@ -27,6 +22,7 @@ import { getNavigationPluginModule } from "@applicaster/zapp-react-native-app/Ap
27
22
  import { RouteManager } from "../RouteManager";
28
23
  import { useScreenConfiguration } from "../River/useScreenConfiguration";
29
24
  import { isValidColor } from "./utils";
25
+ import { useWaitForValidOrientation } from "./hooks";
30
26
 
31
27
  const screenStyles = {
32
28
  flex: 1,
@@ -50,14 +46,6 @@ export function Screen(_props: Props) {
50
46
  const currentScreenData = useCurrentScreenData();
51
47
  const { backgroundColor } = useScreenConfiguration(currentScreenData.id);
52
48
 
53
- const {
54
- width: screenWidth,
55
- height,
56
- deviceInfo,
57
- } = useDimensions("screen", {
58
- fullDimensions: true,
59
- });
60
-
61
49
  const { screenData, pathname } = useRoute();
62
50
 
63
51
  const currentRiver = useScreenData(
@@ -66,13 +54,6 @@ export function Screen(_props: Props) {
66
54
 
67
55
  const { title } = useNavbarState();
68
56
 
69
- const isTablet = deviceInfo?.isTablet;
70
-
71
- const { appData } = usePickFromState(["appData"]);
72
- const isTabletPortrait = appData?.isTabletPortrait;
73
-
74
- const layoutData = { isTablet, isTabletPortrait, width: screenWidth, height };
75
-
76
57
  const hasMenu = shouldNavBarDisplayMenu(currentRiver, plugins);
77
58
 
78
59
  const navBarProps = React.useMemo<MobileNavBarPluginProps | null>(
@@ -108,30 +89,23 @@ export function Screen(_props: Props) {
108
89
  [theme.app_background_color, backgroundColor]
109
90
  );
110
91
 
111
- const targetScreenData =
112
- currentScreenData || (screenData as any)?.targetScreen || screenData;
113
-
114
- const orientation = useGetScreenOrientation(targetScreenData);
92
+ // Set ready state when screen is rotated to desired orientation
93
+ const isReady = useWaitForValidOrientation();
115
94
 
116
95
  // We prevent rendering of the screen until UI is actually rotated to screen desired orientation.
117
96
  // This saves unnecessary re-renders and user will not see distorted aspect screen.
118
- if (
119
- !isOrientationCompatible({
120
- orientation,
121
- layoutData,
122
- })
123
- ) {
124
- return <View style={style} />;
125
- }
126
-
127
97
  return (
128
98
  <View style={style}>
129
- {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
130
-
131
- <OfflineFallbackScreen>
132
- {/* @TODO RouteManager doesn't use props, can they be removed ? */}
133
- <RouteManager pathname={pathname} screenData={screenData} />
134
- </OfflineFallbackScreen>
99
+ {isReady ? (
100
+ <>
101
+ {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
102
+
103
+ <OfflineFallbackScreen>
104
+ {/* @TODO RouteManager doesn't use props, can they be removed ? */}
105
+ <RouteManager pathname={pathname} screenData={screenData} />
106
+ </OfflineFallbackScreen>
107
+ </>
108
+ ) : null}
135
109
  </View>
136
110
  );
137
111
  }
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { equals } from "ramda";
2
3
  import { Animated, ViewProps, ViewStyle } from "react-native";
3
4
  import { useSafeAreaFrame } from "react-native-safe-area-context";
4
5
 
@@ -8,6 +9,11 @@ import { PathnameContext } from "../../Contexts/PathnameContext";
8
9
  import { ScreenDataContext } from "../../Contexts/ScreenDataContext";
9
10
  import { ScreenContextProvider } from "../../Contexts/ScreenContext";
10
11
 
12
+ const fullWidthDimensions = {
13
+ height: "100%",
14
+ width: "100%",
15
+ };
16
+
11
17
  type Props = {
12
18
  children: any;
13
19
  style: any;
@@ -20,6 +26,47 @@ type Props = {
20
26
  screenData: NavigationScreenData;
21
27
  };
22
28
 
29
+ export function CurrentScreenContextProvider({
30
+ children,
31
+ ...props
32
+ }: {
33
+ children: any;
34
+ pathname: string;
35
+ isActive: boolean;
36
+ screenData: NavigationScreenData;
37
+ }) {
38
+ const { pathname, isActive = false, screenData } = props;
39
+
40
+ const [initialScreenData, setInitialScreenData] = React.useState(screenData);
41
+
42
+ React.useLayoutEffect(() => {
43
+ const screenDataChanged = !equals(initialScreenData, screenData);
44
+
45
+ if (isActive && screenDataChanged) {
46
+ setInitialScreenData(screenData);
47
+ }
48
+ }, [initialScreenData, screenData, isActive]);
49
+
50
+ return (
51
+ <ScreenDataContext.Provider value={initialScreenData}>
52
+ <PathnameContext.Provider value={pathname}>
53
+ <ScreenContextProvider pathname={pathname}>
54
+ {children}
55
+ </ScreenContextProvider>
56
+ </PathnameContext.Provider>
57
+ </ScreenDataContext.Provider>
58
+ );
59
+ }
60
+
61
+ const withCurrentScreenContext = (Component) =>
62
+ function SceneComponent(props: Props) {
63
+ return (
64
+ <CurrentScreenContextProvider {...props}>
65
+ <Component {...props} />
66
+ </CurrentScreenContextProvider>
67
+ );
68
+ };
69
+
23
70
  /**
24
71
  * Animated View container for any purpose.
25
72
  * Renders given children with given style object.
@@ -32,53 +79,35 @@ type Props = {
32
79
  * @param contentStyle
33
80
  * @param screenUniqueId
34
81
  */
35
- export function Scene({
82
+ function SceneComponent({
36
83
  children,
37
84
  style,
38
85
  pointerEvents = "auto",
39
86
  overlayStyle,
40
87
  animating,
41
88
  contentStyle,
42
- pathname,
43
89
  isActive = false,
44
90
  screenData,
45
91
  }: Props) {
46
- const [initialScreenData, setInitialScreenData] = React.useState(screenData);
47
-
48
92
  useScreenOrientationHandler({
49
93
  screenData,
50
94
  isActive,
51
95
  });
52
96
 
53
- React.useLayoutEffect(() => {
54
- if (isActive) {
55
- setInitialScreenData(screenData);
56
- }
57
- }, [screenData, isActive]);
58
-
59
97
  const frame = useSafeAreaFrame();
60
98
  const isAnimating = animating && overlayStyle;
61
99
 
62
- const dimensions = isAnimating
63
- ? {
64
- width: "100%",
65
- height: "100%",
66
- }
67
- : frame;
100
+ const dimensions = isAnimating ? fullWidthDimensions : frame;
68
101
 
69
102
  return (
70
103
  <Animated.View
71
104
  pointerEvents={pointerEvents}
72
105
  style={[style, contentStyle, dimensions]}
73
106
  >
74
- <ScreenDataContext.Provider value={initialScreenData}>
75
- <PathnameContext.Provider value={pathname}>
76
- <ScreenContextProvider pathname={pathname}>
77
- {children}
78
- </ScreenContextProvider>
79
- </PathnameContext.Provider>
80
- </ScreenDataContext.Provider>
107
+ {children}
81
108
  {isAnimating && <Animated.View style={overlayStyle} />}
82
109
  </Animated.View>
83
110
  );
84
111
  }
112
+
113
+ export const Scene = withCurrentScreenContext(SceneComponent);
@@ -1,63 +1,79 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<Scene /> renders correctly 1`] = `
4
- <ForwardRef(AnimatedComponentWrapper)
5
- pointerEvents="auto"
4
+ <CurrentScreenContextProvider
5
+ contentStyle={
6
+ {
7
+ "paddingBottom": 49,
8
+ }
9
+ }
10
+ overlayStyle={
11
+ {
12
+ "opacity": 1,
13
+ }
14
+ }
6
15
  style={
7
- [
8
- {
9
- "flex": 1,
10
- },
16
+ {
17
+ "flex": 1,
18
+ }
19
+ }
20
+ >
21
+ <SceneComponent
22
+ contentStyle={
11
23
  {
12
24
  "paddingBottom": 49,
13
- },
25
+ }
26
+ }
27
+ overlayStyle={
14
28
  {
15
- "fontScale": 2,
16
- "height": 1334,
17
- "scale": 2,
18
- "statusBarHeight": null,
19
- "width": 750,
20
- },
21
- ]
22
- }
23
- >
24
- <ContextProvider>
25
- <ContextProvider>
26
- <ScreenContextProvider />
27
- </ContextProvider>
28
- </ContextProvider>
29
- </ForwardRef(AnimatedComponentWrapper)>
29
+ "opacity": 1,
30
+ }
31
+ }
32
+ style={
33
+ {
34
+ "flex": 1,
35
+ }
36
+ }
37
+ />
38
+ </CurrentScreenContextProvider>
30
39
  `;
31
40
 
32
41
  exports[`<Scene /> renders the overlay while animating 1`] = `
33
- <ForwardRef(AnimatedComponentWrapper)
34
- pointerEvents="auto"
42
+ <CurrentScreenContextProvider
43
+ animating={true}
44
+ contentStyle={
45
+ {
46
+ "paddingBottom": 49,
47
+ }
48
+ }
49
+ overlayStyle={
50
+ {
51
+ "opacity": 1,
52
+ }
53
+ }
35
54
  style={
36
- [
37
- {
38
- "flex": 1,
39
- },
55
+ {
56
+ "flex": 1,
57
+ }
58
+ }
59
+ >
60
+ <SceneComponent
61
+ animating={true}
62
+ contentStyle={
40
63
  {
41
64
  "paddingBottom": 49,
42
- },
65
+ }
66
+ }
67
+ overlayStyle={
43
68
  {
44
- "height": "100%",
45
- "width": "100%",
46
- },
47
- ]
48
- }
49
- >
50
- <ContextProvider>
51
- <ContextProvider>
52
- <ScreenContextProvider />
53
- </ContextProvider>
54
- </ContextProvider>
55
- <ForwardRef(AnimatedComponentWrapper)
69
+ "opacity": 1,
70
+ }
71
+ }
56
72
  style={
57
73
  {
58
- "opacity": 1,
74
+ "flex": 1,
59
75
  }
60
76
  }
61
77
  />
62
- </ForwardRef(AnimatedComponentWrapper)>
78
+ </CurrentScreenContextProvider>
63
79
  `;
@@ -9,7 +9,7 @@ exports[`<TransitionerComponent /> renders correctly 1`] = `
9
9
  }
10
10
  }
11
11
  >
12
- <Scene
12
+ <SceneComponent
13
13
  isActive={true}
14
14
  screenUniqueId="mocked-uuid"
15
15
  style={{}}
@@ -23,6 +23,6 @@ exports[`<TransitionerComponent /> renders correctly 1`] = `
23
23
  children
24
24
  </Text>
25
25
  </View>
26
- </Scene>
26
+ </SceneComponent>
27
27
  </View>
28
28
  `;
@@ -1,5 +1,4 @@
1
1
  import * as React from "react";
2
- import { useWindowDimensions } from "react-native";
3
2
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
4
3
  import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks/layout";
5
4
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
@@ -7,10 +6,15 @@ import { TransitionerComponent } from "./Transitioner";
7
6
 
8
7
  export const Transitioner = (props) => {
9
8
  const navigator = useNavigation();
10
- const { height, width } = useWindowDimensions();
11
9
 
12
- const dimensions = useDimensions("window", { deviceInfo: true });
13
- const isTablet = dimensions?.deviceInfo?.isTablet;
10
+ const {
11
+ width,
12
+ height,
13
+ deviceInfo: { isTablet },
14
+ } = useDimensions("window", {
15
+ updateForInactiveScreens: false,
16
+ deviceInfo: true,
17
+ });
14
18
 
15
19
  const { appData } = usePickFromState(["appData"]);
16
20
  const isTabletPortrait = appData?.isTabletPortrait;