-
Notifications
You must be signed in to change notification settings - Fork 2.9k
/
NavigationRoot.js
156 lines (135 loc) · 5.63 KB
/
NavigationRoot.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import React, {useRef, useEffect, useContext} from 'react';
import PropTypes from 'prop-types';
import {NavigationContainer, DefaultTheme, getPathFromState} from '@react-navigation/native';
import {useSharedValue, useAnimatedReaction, interpolateColor, withTiming, withDelay, Easing, runOnJS} from 'react-native-reanimated';
import useFlipper from '../../hooks/useFlipper';
import Navigation, {navigationRef} from './Navigation';
import linkingConfig from './linkingConfig';
import AppNavigator from './AppNavigator';
import themeColors from '../../styles/themes/default';
import Log from '../Log';
import StatusBar from '../StatusBar';
import useCurrentReportID from '../../hooks/useCurrentReportID';
import useWindowDimensions from '../../hooks/useWindowDimensions';
import {SidebarNavigationContext} from '../../pages/home/sidebar/SidebarNavigationContext';
// https://reactnavigation.org/docs/themes
const navigationTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
background: themeColors.appBG,
},
};
const propTypes = {
/** Whether the current user is logged in with an authToken */
authenticated: PropTypes.bool.isRequired,
/** Fired when react-navigation is ready */
onReady: PropTypes.func.isRequired,
};
/**
* Intercept navigation state changes and log it
* @param {NavigationState} state
*/
function parseAndLogRoute(state) {
if (!state) {
return;
}
const currentPath = getPathFromState(state, linkingConfig.config);
// Don't log the route transitions from OldDot because they contain authTokens
if (currentPath.includes('/transition')) {
Log.info('Navigating from transition link from OldDot using short lived authToken');
} else {
Log.info('Navigating to route', false, {path: currentPath});
}
Navigation.setIsNavigationReady();
}
function NavigationRoot(props) {
useFlipper(navigationRef);
const firstRenderRef = useRef(true);
const globalNavigation = useContext(SidebarNavigationContext);
const {updateCurrentReportID} = useCurrentReportID();
const {isSmallScreenWidth} = useWindowDimensions();
useEffect(() => {
if (firstRenderRef.current) {
// we don't want to make the report back button go back to LHN if the user
// started on the small screen so we don't set it on the first render
// making it only work on consecutive changes of the screen size
firstRenderRef.current = false;
return;
}
if (!isSmallScreenWidth) {
return;
}
Navigation.setShouldPopAllStateOnUP();
}, [isSmallScreenWidth]);
useEffect(() => {
if (!navigationRef.isReady() || !props.authenticated) {
return;
}
// We need to force state rehydration so the CustomRouter can add the CentralPaneNavigator route if necessary.
navigationRef.resetRoot(navigationRef.getRootState());
}, [isSmallScreenWidth, props.authenticated]);
const prevStatusBarBackgroundColor = useRef(themeColors.appBG);
const statusBarBackgroundColor = useRef(themeColors.appBG);
const statusBarAnimation = useSharedValue(0);
const updateStatusBarBackgroundColor = (color) => StatusBar.setBackgroundColor(color);
useAnimatedReaction(
() => statusBarAnimation.value,
(current, previous) => {
// Do not run if either of the animated value is null
// or previous animated value is greater than or equal to the current one
if ([current, previous].includes(null) || current <= previous) {
return;
}
const color = interpolateColor(statusBarAnimation.value, [0, 1], [prevStatusBarBackgroundColor.current, statusBarBackgroundColor.current]);
runOnJS(updateStatusBarBackgroundColor)(color);
},
);
const animateStatusBarBackgroundColor = () => {
const currentRoute = navigationRef.getCurrentRoute();
const currentScreenBackgroundColor = (currentRoute.params && currentRoute.params.backgroundColor) || themeColors.PAGE_BACKGROUND_COLORS[currentRoute.name] || themeColors.appBG;
prevStatusBarBackgroundColor.current = statusBarBackgroundColor.current;
statusBarBackgroundColor.current = currentScreenBackgroundColor;
if (currentScreenBackgroundColor === themeColors.appBG && prevStatusBarBackgroundColor.current === themeColors.appBG) {
return;
}
statusBarAnimation.value = 0;
statusBarAnimation.value = withDelay(
300,
withTiming(1, {
duration: 300,
easing: Easing.in,
}),
);
};
const handleStateChange = (state) => {
if (!state) {
return;
}
// Performance optimization to avoid context consumers to delay first render
setTimeout(() => {
updateCurrentReportID(state);
}, 0);
parseAndLogRoute(state);
animateStatusBarBackgroundColor();
// Update the global navigation to show the correct selected menu items.
globalNavigation.updateFromNavigationState(state);
};
return (
<NavigationContainer
onStateChange={handleStateChange}
onReady={props.onReady}
theme={navigationTheme}
ref={navigationRef}
linking={linkingConfig}
documentTitle={{
enabled: false,
}}
>
<AppNavigator authenticated={props.authenticated} />
</NavigationContainer>
);
}
NavigationRoot.displayName = 'NavigationRoot';
NavigationRoot.propTypes = propTypes;
export default NavigationRoot;