Files
standardnotes-app-web/packages/mobile/src/ModalStack.tsx
2022-09-19 14:47:15 -05:00

337 lines
12 KiB
TypeScript

import { RouteProp } from '@react-navigation/native'
import { createStackNavigator, StackNavigationProp } from '@react-navigation/stack'
import { BlockingModal } from '@Root/Components/BlockingModal'
import { HeaderTitleView } from '@Root/Components/HeaderTitleView'
import { IoniconsHeaderButton } from '@Root/Components/IoniconsHeaderButton'
import { Authenticate } from '@Root/Screens/Authenticate/Authenticate'
import { FileInputModal } from '@Root/Screens/InputModal/FileInputModal'
import { PasscodeInputModal } from '@Root/Screens/InputModal/PasscodeInputModal'
import { TagInputModal } from '@Root/Screens/InputModal/TagInputModal'
import { ManageSessions } from '@Root/Screens/ManageSessions/ManageSessions'
import {
MODAL_BLOCKING_ALERT,
SCREEN_AUTHENTICATE,
SCREEN_INPUT_MODAL_FILE_NAME,
SCREEN_INPUT_MODAL_PASSCODE,
SCREEN_INPUT_MODAL_TAG,
SCREEN_INPUT_MODAL_WORKSPACE_NAME,
SCREEN_MANAGE_SESSIONS,
SCREEN_SETTINGS,
SCREEN_UPLOADED_FILES_LIST,
SCREEN_WEB_APP,
} from '@Root/Screens/screens'
import { Settings } from '@Root/Screens/Settings/Settings'
import { UploadedFilesList } from '@Root/Screens/UploadedFilesList/UploadedFilesList'
import { WorkspaceInputModal } from '@Screens/InputModal/WorkspaceInputModal'
import { ApplicationDescriptor, Challenge, DeinitMode, DeinitSource, FileItem, SNNote } from '@standardnotes/snjs'
import { ICON_CHECKMARK, ICON_CLOSE } from '@Style/Icons'
import { ThemeService } from '@Style/ThemeService'
import React, { memo, useContext } from 'react'
import { Platform } from 'react-native'
import { HeaderButtons, Item } from 'react-navigation-header-buttons'
import { ThemeContext } from 'styled-components'
import { ApplicationContext } from './ApplicationContext'
import { AppStackComponent } from './AppStack'
import { HistoryStack } from './HistoryStack'
import { MobileWebAppContainer } from './MobileWebAppContainer'
import { HeaderTitleParams, TEnvironment } from './NativeApp'
export type ModalStackNavigatorParamList = {
AppStack: undefined
HistoryStack: undefined
[SCREEN_SETTINGS]: undefined
[SCREEN_MANAGE_SESSIONS]: undefined
[SCREEN_INPUT_MODAL_TAG]: HeaderTitleParams & {
tagUuid?: string
noteUuid?: string
}
[SCREEN_INPUT_MODAL_FILE_NAME]: HeaderTitleParams & {
file: FileItem
renameFile: (file: FileItem, fileName: string) => Promise<void>
}
[SCREEN_UPLOADED_FILES_LIST]: HeaderTitleParams & {
note: SNNote
}
[SCREEN_INPUT_MODAL_WORKSPACE_NAME]: HeaderTitleParams & {
descriptor: ApplicationDescriptor
renameWorkspace: (descriptor: ApplicationDescriptor, workspaceName: string) => Promise<void>
}
[SCREEN_INPUT_MODAL_PASSCODE]: undefined
[SCREEN_AUTHENTICATE]: {
challenge: Challenge
title?: string
}
[MODAL_BLOCKING_ALERT]: {
title?: string
text: string
}
[SCREEN_WEB_APP]: undefined
}
export type ModalStackNavigationProp<T extends keyof ModalStackNavigatorParamList> = {
navigation: StackNavigationProp<ModalStackNavigatorParamList, T>
route: RouteProp<ModalStackNavigatorParamList, T>
}
const MainStack = createStackNavigator<ModalStackNavigatorParamList>()
export const MobileWebMainStackComponent = () => {
const MemoizedAppStackComponent = memo((props: ModalStackNavigationProp<'AppStack'>) => (
<AppStackComponent {...props} />
))
return (
<MainStack.Navigator
screenOptions={{
gestureEnabled: false,
presentation: 'modal',
}}
initialRouteName="AppStack"
>
<MainStack.Screen
name={'AppStack'}
options={{
headerShown: false,
}}
component={MemoizedAppStackComponent}
/>
</MainStack.Navigator>
)
}
export const NativeMainStackComponent = ({ env }: { env: TEnvironment }) => {
const application = useContext(ApplicationContext)
const theme = useContext(ThemeContext)
const MemoizedAppStackComponent = memo((props: ModalStackNavigationProp<'AppStack'>) => (
<AppStackComponent {...props} />
))
return (
<MainStack.Navigator
screenOptions={{
gestureEnabled: false,
presentation: 'modal',
headerStyle: {
backgroundColor: theme.stylekitContrastBackgroundColor,
},
}}
initialRouteName="AppStack"
>
<MainStack.Screen
name={'AppStack'}
options={{
headerShown: false,
}}
component={MemoizedAppStackComponent}
/>
<MainStack.Screen
options={{
headerShown: false,
}}
name="HistoryStack"
component={HistoryStack}
/>
<MainStack.Screen
name={SCREEN_SETTINGS}
options={() => ({
title: 'Settings',
headerTitle: ({ children }) => {
return <HeaderTitleView title={children || ''} />
},
headerLeft: ({ disabled, onPress }) => (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item
testID="headerButton"
disabled={disabled}
title={Platform.OS === 'ios' ? 'Done' : ''}
iconName={Platform.OS === 'ios' ? undefined : ThemeService.nameForIcon(ICON_CHECKMARK)}
onPress={onPress}
/>
</HeaderButtons>
),
headerRight: () =>
(env === 'dev' || __DEV__) && (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item
testID="headerButton"
title={'Destroy Data'}
onPress={async () => {
await application?.deviceInterface?.removeAllRawStorageValues()
await application?.deviceInterface?.removeAllRawDatabasePayloads(application?.identifier)
application?.deinit(DeinitMode.Soft, DeinitSource.SignOut)
}}
/>
</HeaderButtons>
),
})}
component={Settings}
/>
<MainStack.Screen
name={SCREEN_MANAGE_SESSIONS}
options={() => ({
title: 'Active Sessions',
headerTitle: ({ children }) => {
return <HeaderTitleView title={children || ''} />
},
headerLeft: ({ disabled, onPress }) => (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item
testID="headerButton"
disabled={disabled}
title={Platform.OS === 'ios' ? 'Done' : ''}
iconName={Platform.OS === 'ios' ? undefined : ThemeService.nameForIcon(ICON_CHECKMARK)}
onPress={onPress}
/>
</HeaderButtons>
),
})}
component={ManageSessions}
/>
<MainStack.Screen
name={SCREEN_INPUT_MODAL_PASSCODE}
options={{
title: 'Setup Passcode',
headerTitle: ({ children }) => {
return <HeaderTitleView title={children || ''} />
},
headerLeft: ({ disabled, onPress }) => (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item
testID="headerButton"
disabled={disabled}
title={Platform.OS === 'ios' ? 'Cancel' : ''}
iconName={Platform.OS === 'ios' ? undefined : ThemeService.nameForIcon(ICON_CLOSE)}
onPress={onPress}
/>
</HeaderButtons>
),
}}
component={PasscodeInputModal}
/>
<MainStack.Screen
name={SCREEN_INPUT_MODAL_TAG}
options={({ route }) => ({
title: 'Tag',
gestureEnabled: false,
headerTitle: ({ children }) => {
return <HeaderTitleView title={route.params?.title ?? (children || '')} />
},
headerLeft: ({ disabled, onPress }) => (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item
testID="headerButton"
disabled={disabled}
title={Platform.OS === 'ios' ? 'Cancel' : ''}
iconName={Platform.OS === 'ios' ? undefined : ThemeService.nameForIcon(ICON_CLOSE)}
onPress={onPress}
/>
</HeaderButtons>
),
})}
component={TagInputModal}
/>
<MainStack.Screen
name={SCREEN_INPUT_MODAL_FILE_NAME}
options={({ route }) => ({
title: 'File',
gestureEnabled: false,
headerTitle: ({ children }) => {
return <HeaderTitleView title={route.params?.title ?? (children || '')} />
},
headerLeft: ({ disabled, onPress }) => (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item
testID="headerButton"
disabled={disabled}
title={Platform.OS === 'ios' ? 'Cancel' : ''}
iconName={Platform.OS === 'ios' ? undefined : ThemeService.nameForIcon(ICON_CLOSE)}
onPress={onPress}
/>
</HeaderButtons>
),
})}
component={FileInputModal}
/>
<MainStack.Screen
name={SCREEN_AUTHENTICATE}
options={({ route }) => ({
title: 'Authenticate',
headerLeft: () => undefined,
headerTitle: ({ children }) => <HeaderTitleView title={route.params?.title ?? (children || '')} />,
})}
component={Authenticate}
/>
<MainStack.Screen
name={SCREEN_UPLOADED_FILES_LIST}
options={({ route }) => ({
title: 'Files',
headerLeft: ({ disabled, onPress }) => (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item
testID="headerButton"
disabled={disabled}
title={Platform.OS === 'ios' ? 'Close' : ''}
iconName={Platform.OS === 'ios' ? undefined : ThemeService.nameForIcon(ICON_CLOSE)}
onPress={onPress}
/>
</HeaderButtons>
),
headerTitle: ({ children }) => {
return <HeaderTitleView title={route.params?.title ?? (children || '')} />
},
})}
component={UploadedFilesList}
/>
<MainStack.Screen
name={MODAL_BLOCKING_ALERT}
options={() => ({
headerShown: false,
cardStyle: { backgroundColor: 'rgba(0, 0, 0, 0.15)' },
cardOverlayEnabled: true,
cardStyleInterpolator: ({ current: { progress } }) => ({
cardStyle: {
opacity: progress.interpolate({
inputRange: [0, 0.5, 0.9, 1],
outputRange: [0, 0.25, 0.7, 1],
}),
},
overlayStyle: {
opacity: progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 0.5],
extrapolate: 'clamp',
}),
},
}),
})}
component={BlockingModal}
/>
<MainStack.Screen
name={SCREEN_INPUT_MODAL_WORKSPACE_NAME}
options={({ route }) => ({
title: 'Workspace',
gestureEnabled: false,
headerTitle: ({ children }) => {
return <HeaderTitleView title={route.params?.title ?? (children || '')} />
},
headerLeft: ({ disabled, onPress }) => (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item
testID="headerButton"
disabled={disabled}
title={Platform.OS === 'ios' ? 'Cancel' : ''}
iconName={Platform.OS === 'ios' ? undefined : ThemeService.nameForIcon(ICON_CLOSE)}
onPress={onPress}
/>
</HeaderButtons>
),
})}
component={WorkspaceInputModal}
/>
<MainStack.Screen name={SCREEN_WEB_APP} options={() => ({ title: 'App' })} component={MobileWebAppContainer} />
</MainStack.Navigator>
)
}