import React, { useEffect, useRef, useState } from 'react'; import { Dimensions, Linking, unstable_batchedUpdates } from 'react-native'; import { AppearanceProvider } from 'react-native-appearance'; import { KeyCommandsEmitter } from 'react-native-keycommands'; import { initialWindowMetrics, SafeAreaProvider } from 'react-native-safe-area-context'; import RNScreens from 'react-native-screens'; import { Provider } from 'react-redux'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app'; import { deepLinkingOpen } from './actions/deepLinking'; import AppContainer from './AppContainer'; import { KEY_COMMAND } from './commands'; import { ActionSheetProvider } from './containers/ActionSheet'; import InAppNotification from './containers/InAppNotification'; import Toast from './containers/Toast'; import TwoFactor from './containers/TwoFactor'; import { ICommand } from './definitions/ICommand'; import { IThemePreference } from './definitions/ITheme'; import { DimensionsContext } from './dimensions'; import { colors, isFDroidBuild, MIN_WIDTH_MASTER_DETAIL_LAYOUT, themes } from './lib/constants'; import { getAllowAnalyticsEvents, getAllowCrashReport } from './lib/methods'; import parseQuery from './lib/methods/helpers/parseQuery'; import { initializePushNotifications, onNotification } from './lib/notifications'; import store from './lib/store'; import { initStore } from './lib/store/auxStore'; import { ThemeContext } from './theme'; import { debounce, isTablet } from './lib/methods/helpers'; import EventEmitter from './lib/methods/helpers/events'; import { toggleAnalyticsEventsReport, toggleCrashErrorsReport } from './lib/methods/helpers/log'; import { getTheme, initialTheme, newThemeState, setNativeTheme, subscribeTheme, unsubscribeTheme } from './lib/methods/helpers/theme'; import ChangePasscodeView from './views/ChangePasscodeView'; import ScreenLockedView from './views/ScreenLockedView'; RNScreens.enableScreens(); initStore(store); interface IDimensions { width: number; height: number; scale: number; fontScale: number; } const parseDeepLinking = (url: string) => { if (url) { url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, ''); const regex = /^(room|auth|invite)\?/; if (url.match(regex)) { url = url.replace(regex, '').trim(); if (url) { return parseQuery(url); } } const call = /^(https:\/\/)?jitsi.rocket.chat\//; const fullURL = url; if (url.match(call)) { url = url.replace(call, '').trim(); if (url) { return { path: url, isCall: true, fullURL }; } } } return null; }; const { width: widthWindow, height: heightWindow, scale: scaleWindow, fontScale: fontScaleWindow } = Dimensions.get('window'); const Root = () => { const [theme, setTheme] = useState(getTheme(initialTheme())); const [themePreferences, setThemePreferences] = useState(initialTheme()); const [width, setWidth] = useState(widthWindow); const [height, setHeight] = useState(heightWindow); const [scale, setScale] = useState(scaleWindow); const [fontScale, setFontScale] = useState(fontScaleWindow); const listenerTimeout = useRef(); const onKeyCommands = useRef(); useEffect(() => { init(); if (!isFDroidBuild) { initCrashReport(); } const theme = initialTheme(); if (isTablet) { initTablet(); } setNativeTheme(theme); listenerTimeout.current = setTimeout(() => { Linking.addEventListener('url', ({ url }) => { const parsedDeepLinkingURL = parseDeepLinking(url); if (parsedDeepLinkingURL) { store.dispatch(deepLinkingOpen(parsedDeepLinkingURL)); } }); }, 5000); Dimensions.addEventListener('change', val => { onDimensionsChange(val); console.log('🚀 ~ file: index.tsx ~ line 109 ~ Dimensions.addEventListener ~ val', val); }); return () => { clearTimeout(listenerTimeout.current); Dimensions.removeEventListener('change', onDimensionsChange); unsubscribeTheme(); if (onKeyCommands?.current?.remove) { onKeyCommands.current.remove?.(); } }; }, []); const init = async () => { store.dispatch(appInitLocalSettings()); // Open app from push notification const notification = await initializePushNotifications(); if (notification) { onNotification(notification); return; } // Open app from deep linking const deepLinking = await Linking.getInitialURL(); const parsedDeepLinkingURL = parseDeepLinking(deepLinking!); if (parsedDeepLinkingURL) { store.dispatch(deepLinkingOpen(parsedDeepLinkingURL)); return; } // Open app from app icon store.dispatch(appInit()); }; const getMasterDetail = (width: number) => { if (!isTablet) { return false; } return width > MIN_WIDTH_MASTER_DETAIL_LAYOUT; }; const setMasterDetail = (width: number) => { const isMasterDetail = getMasterDetail(width); store.dispatch(setMasterDetailAction(isMasterDetail)); }; // Dimensions update fires twice const onDimensionsChange = debounce(({ window: { width, height, scale, fontScale } }: { window: IDimensions }) => { console.log('🚀 ~ file: index.tsx ~ line 160 ~ onDimensionsChange ~ { width, height, scale, fontScale }', { width, height, scale, fontScale }); setDimensions({ width, height, scale, fontScale }); setMasterDetail(width); }); const setThemeFunction = (newThemeObject = {}) => { const { theme: newTheme, themePreferences: newThemePreferences } = newThemeState( themePreferences, newThemeObject as IThemePreference ); setThemePreferences(newThemePreferences); setTheme(newTheme); subscribeTheme(themePreferences, setThemeFunction); }; const setDimensions = ({ width, height, scale, fontScale }: IDimensions) => { // Test when migrate to React 18 https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching // We need this batchedUpdates because we are calling this from a debounce and // the React isn't able to batch multiples setState after asynchronous function // https://stackoverflow.com/a/48610973 unstable_batchedUpdates(() => { setWidth(width); setHeight(height); setScale(scale); setFontScale(fontScale); }); }; const initTablet = () => { setMasterDetail(width); onKeyCommands.current = KeyCommandsEmitter.addListener('onKeyCommand', (command: ICommand) => { EventEmitter.emit(KEY_COMMAND, { event: command }); }); }; const initCrashReport = () => { getAllowCrashReport().then(allowCrashReport => { toggleCrashErrorsReport(allowCrashReport); }); getAllowAnalyticsEvents().then(allowAnalyticsEvents => { toggleAnalyticsEventsReport(allowAnalyticsEvents); }); }; console.count('🤯 App/index'); return ( ); }; export default Root;