import React, { useContext } from 'react'; import { Dimensions } from 'react-native'; import PropTypes from 'prop-types'; import { NavigationContainer } from '@react-navigation/native'; import { AppearanceProvider } from 'react-native-appearance'; import { createStackNavigator } from '@react-navigation/stack'; import { Provider } from 'react-redux'; import RNUserDefaults from 'rn-user-defaults'; import { defaultTheme, newThemeState, subscribeTheme, unsubscribeTheme } from './utils/theme'; import Navigation from './lib/ShareNavigation'; import store from './lib/createStore'; import { supportSystemTheme } from './utils/deviceInfo'; import { defaultHeader, themedHeader, getActiveRouteName, navigationTheme } from './utils/navigation'; import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat'; import { ThemeContext } from './theme'; import { localAuthenticate } from './utils/localAuthentication'; import ScreenLockedView from './views/ScreenLockedView'; // Outside Stack import WithoutServersView from './views/WithoutServersView'; // Inside Stack import ShareListView from './views/ShareListView'; import ShareView from './views/ShareView'; import SelectServerView from './views/SelectServerView'; import { setCurrentScreen } from './utils/log'; import AuthLoadingView from './views/AuthLoadingView'; import { DimensionsContext } from './dimensions'; import debounce from './utils/debounce'; const Inside = createStackNavigator(); const InsideStack = () => { const { theme } = useContext(ThemeContext); const screenOptions = { ...defaultHeader, ...themedHeader(theme) }; screenOptions.headerStyle = { ...screenOptions.headerStyle, height: 57 }; return ( <Inside.Navigator screenOptions={screenOptions}> <Inside.Screen name='ShareListView' component={ShareListView} /> <Inside.Screen name='ShareView' component={ShareView} /> <Inside.Screen name='SelectServerView' component={SelectServerView} options={SelectServerView.navigationOptions} /> </Inside.Navigator> ); }; const Outside = createStackNavigator(); const OutsideStack = () => { const { theme } = useContext(ThemeContext); return ( <Outside.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme) }}> <Outside.Screen name='WithoutServersView' component={WithoutServersView} options={WithoutServersView.navigationOptions} /> </Outside.Navigator> ); }; // App const Stack = createStackNavigator(); export const App = ({ root }) => ( <Stack.Navigator screenOptions={{ headerShown: false, animationEnabled: false }}> <> {!root ? ( <Stack.Screen name='AuthLoading' component={AuthLoadingView} /> ) : null} {root === 'outside' ? ( <Stack.Screen name='OutsideStack' component={OutsideStack} /> ) : null} {root === 'inside' ? ( <Stack.Screen name='InsideStack' component={InsideStack} /> ) : null} </> </Stack.Navigator> ); App.propTypes = { root: PropTypes.string }; class Root extends React.Component { constructor(props) { super(props); const { width, height, scale } = Dimensions.get('screen'); this.state = { theme: defaultTheme(), themePreferences: { currentTheme: supportSystemTheme() ? 'automatic' : 'light', darkLevel: 'dark' }, root: '', width, height, scale }; this.init(); } componentWillUnmount() { RocketChat.closeShareExtension(); unsubscribeTheme(); } init = async() => { RNUserDefaults.objectForKey(THEME_PREFERENCES_KEY).then(this.setTheme); const currentServer = await RNUserDefaults.get('currentServer'); const token = await RNUserDefaults.get(RocketChat.TOKEN_KEY); if (currentServer && token) { await localAuthenticate(currentServer); this.setState({ root: 'inside' }); await RocketChat.shareExtensionInit(currentServer); } else { this.setState({ root: 'outside' }); } const state = Navigation.navigationRef.current?.getRootState(); const currentRouteName = getActiveRouteName(state); Navigation.routeNameRef.current = currentRouteName; setCurrentScreen(currentRouteName); } setTheme = (newTheme = {}) => { // change theme state this.setState(prevState => newThemeState(prevState, newTheme), () => { const { themePreferences } = this.state; // subscribe to Appearance changes subscribeTheme(themePreferences, this.setTheme); }); } // Dimensions update fires twice onDimensionsChange = debounce(({ window: { width, height, scale } }) => { this.setDimensions({ width, height, scale }); this.setMasterDetail(width); }) setDimensions = ({ width, height, scale }) => { this.setState({ width, height, scale }); } render() { const { theme, root, width, height, scale } = this.state; const navTheme = navigationTheme(theme); return ( <AppearanceProvider> <Provider store={store}> <ThemeContext.Provider value={{ theme }}> <DimensionsContext.Provider value={{ width, height, scale, setDimensions: this.setDimensions }} > <NavigationContainer theme={navTheme} ref={Navigation.navigationRef} onStateChange={(state) => { const previousRouteName = Navigation.routeNameRef.current; const currentRouteName = getActiveRouteName(state); if (previousRouteName !== currentRouteName) { setCurrentScreen(currentRouteName); } Navigation.routeNameRef.current = currentRouteName; }} > <App root={root} /> </NavigationContainer> <ScreenLockedView /> </DimensionsContext.Provider> </ThemeContext.Provider> </Provider> </AppearanceProvider> ); } } export default Root;