import { HeaderLeft } from '@components/Header' import { displayMessage, Message } from '@components/Message' import navigationRef from '@helpers/navigationRef' import { NavigationContainer } from '@react-navigation/native' import { createNativeStackNavigator } from '@react-navigation/native-stack' import ScreenActions from '@screens/Actions' import ScreenAnnouncements from '@screens/Announcements' import ScreenCompose from '@screens/Compose' import ScreenImagesViewer from '@screens/ImagesViewer' import ScreenTabs from '@screens/Tabs' import initQuery from '@utils/initQuery' import { RootStackParamList } from '@utils/navigation/navigators' import pushUseConnect from '@utils/push/useConnect' import pushUseReceive from '@utils/push/useReceive' import pushUseRespond from '@utils/push/useRespond' import { updatePreviousTab } from '@utils/slices/contextsSlice' import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences' import { updateConfiguration } from '@utils/slices/instances/updateConfiguration' import { updateFilters } from '@utils/slices/instances/updateFilters' import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice' import { useTheme } from '@utils/styles/ThemeManager' import { themes } from '@utils/styles/themes' import * as Analytics from 'expo-firebase-analytics' import * as Linking from 'expo-linking' import { addScreenshotListener } from 'expo-screen-capture' import React, { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { Alert, Platform, StatusBar } from 'react-native' import { useQueryClient } from 'react-query' import { useDispatch, useSelector } from 'react-redux' import * as Sentry from 'sentry-expo' const Stack = createNativeStackNavigator() export interface Props { localCorrupt?: string } const Screens: React.FC = ({ localCorrupt }) => { const { t } = useTranslation('screens') const dispatch = useDispatch() const instanceActive = useSelector(getInstanceActive) const { mode, theme } = useTheme() enum barStyle { light = 'dark-content', dark = 'light-content' } const routeRef = useRef<{ name?: string; params?: {} }>() // Push hooks const instances = useSelector( getInstances, (prev, next) => prev.length === next.length ) const queryClient = useQueryClient() pushUseConnect({ t, instances }) pushUseReceive({ instances }) pushUseRespond({ instances }) // Prevent screenshot alert useEffect(() => { const screenshotListener = addScreenshotListener(() => Alert.alert(t('screenshot.title'), t('screenshot.message'), [ { text: t('screenshot.button'), style: 'destructive' } ]) ) Platform.select({ ios: screenshotListener }) return () => screenshotListener.remove() }, []) // On launch display login credentials corrupt information useEffect(() => { const showLocalCorrect = () => { if (localCorrupt) { displayMessage({ message: t('localCorrupt.message'), description: localCorrupt.length ? localCorrupt : undefined, type: 'error', mode }) navigationRef.navigate('Screen-Tabs', { screen: 'Tab-Me' }) } } return showLocalCorrect() }, [localCorrupt]) // Lazily update users's preferences, for e.g. composing default visibility useEffect(() => { if (instanceActive !== -1) { dispatch(updateConfiguration()) dispatch(updateFilters()) dispatch(updateAccountPreferences()) } }, [instanceActive]) // Callbacks const navigationContainerOnReady = useCallback(() => { const currentRoute = navigationRef.getCurrentRoute() routeRef.current = { name: currentRoute?.name, params: currentRoute?.params ? JSON.stringify(currentRoute.params) : undefined } }, []) const navigationContainerOnStateChange = useCallback(() => { const previousRoute = routeRef.current const currentRoute = navigationRef.getCurrentRoute() const matchTabName = currentRoute?.name?.match(/(Tab-.*)-Root/) if (matchTabName) { //@ts-ignore dispatch(updatePreviousTab(matchTabName[1])) } if (previousRoute?.name !== currentRoute?.name) { Analytics.logEvent('screen_view', { screen_name: currentRoute?.name }) Sentry.Native.setContext('page', { previous: previousRoute, current: currentRoute }) } routeRef.current = currentRoute }, []) // Deep linking for compose const [deeplinked, setDeeplinked] = useState(false) useEffect(() => { const getUrlAsync = async () => { setDeeplinked(true) const initialUrl = await Linking.parseInitialURLAsync() if (initialUrl.path) { const paths = initialUrl.path.split('/') if (paths && paths.length) { const instanceIndex = instances.findIndex( instance => paths[0] === `@${instance.account.acct}@${instance.uri}` ) if (instanceIndex !== -1 && instanceActive !== instanceIndex) { initQuery({ instance: instances[instanceIndex], prefetch: { enabled: true } }) } } } if (initialUrl.hostname === 'compose') { navigationRef.navigate('Screen-Compose') } } if (!deeplinked) { getUrlAsync() } }, [instanceActive, instances, deeplinked]) return ( <> ({ presentation: 'transparentModal', animation: 'fade', headerShown: true, headerShadowVisible: false, headerTransparent: true, headerStyle: { backgroundColor: 'transparent' }, headerLeft: () => ( navigation.goBack()} /> ), title: t('screenAnnouncements:heading') })} /> ) } export default React.memo(Screens, () => true)