import { AppStateEventType, AppStateType, TabletModeChangeData } from '@Lib/ApplicationState' import { useHasEditor, useIsLocked } from '@Lib/SnjsHelperHooks' import { ScreenStatus } from '@Lib/StatusManager' import { IsDev } from '@Lib/Utils' import { CompositeNavigationProp, RouteProp } from '@react-navigation/native' import { createStackNavigator, StackNavigationProp } from '@react-navigation/stack' import { HeaderTitleView } from '@Root/Components/HeaderTitleView' import { IoniconsHeaderButton } from '@Root/Components/IoniconsHeaderButton' import { Compose } from '@Root/Screens/Compose/Compose' import { SCREEN_COMPOSE, SCREEN_NOTES, SCREEN_VIEW_PROTECTED_NOTE } from '@Root/Screens/screens' import { MainSideMenu } from '@Root/Screens/SideMenu/MainSideMenu' import { NoteSideMenu } from '@Root/Screens/SideMenu/NoteSideMenu' import { ViewProtectedNote } from '@Root/Screens/ViewProtectedNote/ViewProtectedNote' import { Root } from '@Screens/Root' import { UuidString } from '@standardnotes/snjs' import { ICON_MENU } from '@Style/Icons' import { ThemeService } from '@Style/ThemeService' import { getDefaultDrawerWidth } from '@Style/Utils' import React, { useCallback, useContext, useEffect, useRef, useState } from 'react' import { Dimensions, Keyboard, ScaledSize } from 'react-native' import DrawerLayout, { DrawerState } from 'react-native-gesture-handler/DrawerLayout' import { HeaderButtons, Item } from 'react-navigation-header-buttons' import { ThemeContext } from 'styled-components' import { HeaderTitleParams } from './App' import { ApplicationContext } from './ApplicationContext' import { MobileWebAppContainer } from './MobileWebAppContainer' import { ModalStackNavigationProp } from './ModalStack' const IS_DEBUGGING_WEB_APP = false const DEFAULT_TO_WEB_APP = IsDev && IS_DEBUGGING_WEB_APP export type AppStackNavigatorParamList = { [SCREEN_NOTES]: HeaderTitleParams [SCREEN_COMPOSE]: HeaderTitleParams & { noteUuid: UuidString } [SCREEN_VIEW_PROTECTED_NOTE]: { onPressView: () => void } } export type AppStackNavigationProp = { navigation: CompositeNavigationProp< ModalStackNavigationProp<'AppStack'>['navigation'], StackNavigationProp > route: RouteProp } export const AppStack = createStackNavigator() export const AppStackComponent = (props: ModalStackNavigationProp<'AppStack'>) => { // Context const application = useContext(ApplicationContext) const theme = useContext(ThemeContext) const [isLocked] = useIsLocked() const [hasEditor] = useHasEditor() // State const [dimensions, setDimensions] = useState(() => Dimensions.get('window')) const [isInTabletMode, setIsInTabletMode] = useState(() => application?.getAppState().isInTabletMode) const [notesStatus, setNotesStatus] = useState() const [composeStatus, setComposeStatus] = useState() const [noteDrawerOpen, setNoteDrawerOpen] = useState(false) // Ref const drawerRef = useRef(null) const noteDrawerRef = useRef(null) useEffect(() => { const removeObserver = application?.getAppState().addStateChangeObserver((event) => { if (event === AppStateType.EditorClosed) { noteDrawerRef.current?.closeDrawer() if (!isInTabletMode && props.navigation.canGoBack()) { props.navigation.popToTop() } } }) return removeObserver }, [application, props.navigation, isInTabletMode]) useEffect(() => { const removeObserver = application?.getStatusManager().addHeaderStatusObserver((messages) => { setNotesStatus(messages[SCREEN_NOTES]) setComposeStatus(messages[SCREEN_COMPOSE]) }) return removeObserver }, [application, isInTabletMode]) useEffect(() => { const updateDimensions = ({ window }: { window: ScaledSize }) => { setDimensions(window) } const removeDimensionsChangeListener = Dimensions.addEventListener('change', updateDimensions) return () => removeDimensionsChangeListener.remove() }, []) useEffect(() => { const remoteTabletModeSubscription = application?.getAppState().addStateEventObserver((event, data) => { if (event === AppStateEventType.TabletModeChange) { const eventData = data as TabletModeChangeData if (eventData.new_isInTabletMode && !eventData.old_isInTabletMode) { setIsInTabletMode(true) } else if (!eventData.new_isInTabletMode && eventData.old_isInTabletMode) { setIsInTabletMode(false) } } }) return remoteTabletModeSubscription }, [application]) const handleDrawerStateChange = useCallback( (newState: DrawerState, drawerWillShow: boolean) => { if (newState !== 'Idle' && drawerWillShow) { application?.getAppState().onDrawerOpen() } }, [application], ) return ( !isLocked && } > setNoteDrawerOpen(true)} onDrawerClose={() => setNoteDrawerOpen(false)} drawerPosition={'right'} drawerType="slide" drawerLockMode={hasEditor ? 'unlocked' : 'locked-closed'} renderNavigationView={() => hasEditor && } > ({ headerStyle: { backgroundColor: theme.stylekitContrastBackgroundColor, }, headerTintColor: theme.stylekitInfoColor, headerTitle: ({ children }) => { return }, })} initialRouteName={SCREEN_NOTES} > ({ title: 'All notes', headerTitle: ({ children }) => { const screenStatus = isInTabletMode ? composeStatus || notesStatus : notesStatus const title = route.params?.title ?? (children || '') const subtitle = [screenStatus?.status, route.params?.subTitle].filter((x) => !!x).join(' • ') return }, headerLeft: () => ( { Keyboard.dismiss() drawerRef.current?.openDrawer() }} /> ), headerRight: () => isInTabletMode && hasEditor && ( { Keyboard.dismiss() noteDrawerRef.current?.openDrawer() }} /> ), })} component={DEFAULT_TO_WEB_APP ? MobileWebAppContainer : Root} /> ({ headerTitle: ({ children }) => { return ( ) }, headerRight: () => !isInTabletMode && ( { Keyboard.dismiss() noteDrawerRef.current?.openDrawer() }} /> ), })} component={Compose} /> ({ title: 'View Protected Note', })} component={ViewProtectedNote} /> ) }