// @flow import * as React from 'react'; import {LogBox, Platform, SafeAreaView, StatusBar, View} from 'react-native'; import LocaleManager from './src/managers/LocaleManager'; import AsyncStorageManager from "./src/managers/AsyncStorageManager"; import CustomIntroSlider from "./src/components/Overrides/CustomIntroSlider"; import type {CustomTheme} from "./src/managers/ThemeManager"; import ThemeManager from './src/managers/ThemeManager'; import {NavigationContainer} from '@react-navigation/native'; import MainNavigator from './src/navigation/MainNavigator'; import {Provider as PaperProvider} from 'react-native-paper'; import AprilFoolsManager from "./src/managers/AprilFoolsManager"; import Update from "./src/constants/Update"; import ConnectionManager from "./src/managers/ConnectionManager"; import URLHandler from "./src/utils/URLHandler"; import {setSafeBounceHeight} from "react-navigation-collapsible"; import SplashScreen from 'react-native-splash-screen' import {OverflowMenuProvider} from "react-navigation-header-buttons"; // Native optimizations https://reactnavigation.org/docs/react-native-screens // Crashes app when navigating away from webview on android 9+ // enableScreens(true); LogBox.ignoreLogs([ // collapsible headers cause this warning, just ignore as it is not an issue 'Non-serializable values were found in the navigation state', 'Cannot update a component from inside the function body of a different component', ]); type Props = {}; type State = { isLoading: boolean, showIntro: boolean, showUpdate: boolean, showAprilFools: boolean, currentTheme: CustomTheme | null, }; export default class App extends React.Component { state = { isLoading: true, showIntro: true, showUpdate: true, showAprilFools: false, currentTheme: null, }; navigatorRef: { current: null | NavigationContainer }; defaultHomeRoute: string | null; defaultHomeData: { [key: string]: any } createDrawerNavigator: () => React.Node; urlHandler: URLHandler; constructor() { super(); LocaleManager.initTranslations(); this.navigatorRef = React.createRef(); this.defaultHomeRoute = null; this.defaultHomeData = {}; this.urlHandler = new URLHandler(this.onInitialURLParsed, this.onDetectURL); this.urlHandler.listen(); setSafeBounceHeight(Platform.OS === 'ios' ? 100 : 20); this.loadAssetsAsync().then(() => { this.onLoadFinished(); }); } /** * The app has been started by an url, and it has been parsed. * Set a new default start route based on the data parsed. * * @param parsedData The data parsed from the url */ onInitialURLParsed = (parsedData: { route: string, data: { [key: string]: any } }) => { this.defaultHomeRoute = parsedData.route; this.defaultHomeData = parsedData.data; }; /** * An url has been opened and parsed while the app was active. * Redirect the user to the screen according to parsed data. * * @param parsedData The data parsed from the url */ onDetectURL = (parsedData: { route: string, data: { [key: string]: any } }) => { // Navigate to nested navigator and pass data to the index screen if (this.navigatorRef.current != null) { this.navigatorRef.current.navigate('home', { screen: 'index', params: {nextScreen: parsedData.route, data: parsedData.data} }); } }; /** * Updates the current theme */ onUpdateTheme = () => { this.setState({ currentTheme: ThemeManager.getCurrentTheme() }); this.setupStatusBar(); }; /** * Updates status bar content color if on iOS only, * as the android status bar is always set to black. */ setupStatusBar() { if (ThemeManager.getNightMode()) { StatusBar.setBarStyle('light-content', true); } else { StatusBar.setBarStyle('dark-content', true); } if (Platform.OS === "android") StatusBar.setBackgroundColor(ThemeManager.getCurrentTheme().colors.surface, true); } /** * Callback when user ends the intro. Save in preferences to avoid showing back the introSlides */ onIntroDone = () => { this.setState({ showIntro: false, showUpdate: false, showAprilFools: false, }); AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.showIntro.key, false); AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.updateNumber.key, Update.number); AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key, false); }; /** * Loads every async data * * @returns {Promise} */ loadAssetsAsync = async () => { await AsyncStorageManager.getInstance().loadPreferences(); try { await ConnectionManager.getInstance().recoverLogin(); } catch (e) { } } /** * Async loading is done, finish processing startup data */ onLoadFinished() { // Only show intro if this is the first time starting the app this.createDrawerNavigator = () => ; ThemeManager.getInstance().setUpdateThemeCallback(this.onUpdateTheme); // Status bar goes dark if set too fast on ios if (Platform.OS === 'ios') setTimeout(this.setupStatusBar, 1000); else this.setupStatusBar(); this.setState({ isLoading: false, currentTheme: ThemeManager.getCurrentTheme(), showIntro: AsyncStorageManager.getBool(AsyncStorageManager.PREFERENCES.showIntro.key), showUpdate: AsyncStorageManager.getNumber(AsyncStorageManager.PREFERENCES.updateNumber.key) !== Update.number, showAprilFools: AprilFoolsManager.getInstance().isAprilFoolsEnabled() && AsyncStorageManager.getBool(AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key), }); SplashScreen.hide(); } /** * Renders the app based on loading state */ render() { if (this.state.isLoading) { return null; } else if (this.state.showIntro || this.state.showUpdate || this.state.showAprilFools) { return ; } else { return ( ); } } }