diff --git a/package-lock.json b/package-lock.json index 7bd9a7ef2..29d564e76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,6 @@ "@react-native-picker/picker": "^2.11.1", "@react-native-vector-icons/common": "^12.3.0", "@react-navigation/bottom-tabs": "^7.4.6", - "@react-navigation/drawer": "^7.5.7", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.17", "@react-navigation/native-stack": "^7.3.25", @@ -6412,36 +6411,6 @@ "react": ">=16.8" } }, - "node_modules/@react-navigation/drawer": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-7.5.7.tgz", - "integrity": "sha512-iRmeFUMZ4DPYgiuPVKsohL40flGAO0rxWwOg4iWkh0DsglI9yKpDXTUsUjmY4bMTv61jYYWV92OcSCBJjXwJoA==", - "license": "MIT", - "dependencies": { - "@react-navigation/elements": "^2.6.3", - "color": "^4.2.3", - "react-native-drawer-layout": "^4.1.12", - "use-latest-callback": "^0.2.4" - }, - "peerDependencies": { - "@react-navigation/native": "^7.1.17", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-gesture-handler": ">= 2.0.0", - "react-native-reanimated": ">= 2.0.0", - "react-native-safe-area-context": ">= 4.0.0", - "react-native-screens": ">= 4.0.0" - } - }, - "node_modules/@react-navigation/drawer/node_modules/use-latest-callback": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.4.tgz", - "integrity": "sha512-LS2s2n1usUUnDq4oVh1ca6JFX9uSqUncTfAm44WMg0v6TxL7POUTk1B044NH8TeLkFbNajIsgDHcgNpNzZucdg==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8" - } - }, "node_modules/@react-navigation/elements": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.6.3.tgz", @@ -19888,28 +19857,6 @@ "react-native-reanimated": ">=2.8.0" } }, - "node_modules/react-native-drawer-layout": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/react-native-drawer-layout/-/react-native-drawer-layout-4.1.12.tgz", - "integrity": "sha512-oKolvp5seiUieG+RHGjpFe8rH8Ds24iW0QBl31TlCVOX7tdn42IQIBl5tuD1i7h3q+VqqnbcT+NB2dcJ5suZkw==", - "dependencies": { - "use-latest-callback": "^0.2.4" - }, - "peerDependencies": { - "react": ">= 18.2.0", - "react-native": "*", - "react-native-gesture-handler": ">= 2.0.0", - "react-native-reanimated": ">= 2.0.0" - } - }, - "node_modules/react-native-drawer-layout/node_modules/use-latest-callback": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.4.tgz", - "integrity": "sha512-LS2s2n1usUUnDq4oVh1ca6JFX9uSqUncTfAm44WMg0v6TxL7POUTk1B044NH8TeLkFbNajIsgDHcgNpNzZucdg==", - "peerDependencies": { - "react": ">=16.8" - } - }, "node_modules/react-native-event-listeners": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/react-native-event-listeners/-/react-native-event-listeners-1.0.7.tgz", diff --git a/package.json b/package.json index d9796fc6a..e54c89987 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,6 @@ "@react-native-picker/picker": "^2.11.1", "@react-native-vector-icons/common": "^12.3.0", "@react-navigation/bottom-tabs": "^7.4.6", - "@react-navigation/drawer": "^7.5.7", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.17", "@react-navigation/native-stack": "^7.3.25", diff --git a/src/components/App.js b/src/components/App.js index 7bb2c1c08..b128d127c 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -1,7 +1,7 @@ // @flow import { useNavigation } from "@react-navigation/native"; -import RootDrawerNavigator from "navigation/rootDrawerNavigator"; +import RootStackNavigator from "navigation/RootStackNavigator"; import type { Node } from "react"; import React, { useCallback } from "react"; import { log } from "sharedHelpers/logger"; @@ -80,7 +80,7 @@ const App = ( { children }: Props ): Node => { - {children || } + {children || } ); }; diff --git a/src/navigation/CustomDrawerContent.tsx b/src/navigation/CustomDrawerContent.tsx deleted file mode 100644 index 0c023f43e..000000000 --- a/src/navigation/CustomDrawerContent.tsx +++ /dev/null @@ -1,348 +0,0 @@ -import { useNetInfo } from "@react-native-community/netinfo"; -import { - DrawerContentScrollView, - DrawerItem -} from "@react-navigation/drawer"; -import { useQueryClient } from "@tanstack/react-query"; -import classnames from "classnames"; -import { - signOut -} from "components/LoginSignUp/AuthenticationService"; -import { - Body1, - Heading4, - INatIcon, - INatIconButton, - List2, TextInputSheet, - UserIcon, - WarningSheet -} from "components/SharedComponents"; -import { Pressable, View } from "components/styledComponents"; -import { RealmContext } from "providers/contexts"; -import React, { useCallback, useMemo, useState } from "react"; -import type { ViewStyle } from "react-native"; -import { - Alert, Dimensions -} from "react-native"; -import User from "realmModels/User"; -import { BREAKPOINTS } from "sharedHelpers/breakpoint"; -import { log } from "sharedHelpers/logger"; -import { useCurrentUser, useTranslation } from "sharedHooks"; -import useStore, { zustandStorage } from "stores/useStore"; -import colors from "styles/tailwindColors"; - -const { useRealm } = RealmContext; - -const { width } = Dimensions.get( "screen" ); - -function isDefaultMode( ) { - return useStore.getState( ).layout.isDefaultMode === true; -} - -const createDrawerStyle = ( isDark: boolean ) => ( { - backgroundColor: isDark - ? colors.darkModeGray - : "white", - borderTopRightRadius: 20, - borderBottomRightRadius: 20, - minHeight: "100%" -} as const ); - -interface Props { - state: object; - navigation: object; - descriptors: object; - colorScheme?: string; -} - -const feedbackLogger = log.extend( "feedback" ); - -function showOfflineAlert( t ) { - Alert.alert( t( "You-are-offline" ), t( "Please-try-again-when-you-are-online" ) ); -} - -const CustomDrawerContent = ( { - state, navigation, descriptors, colorScheme -}: Props ) => { - const isDebug = zustandStorage.getItem( "debugMode" ) === "true"; - const isDarkMode = colorScheme === "dark" && isDebug; - const drawerScrollViewStyle = createDrawerStyle( isDarkMode ); - const realm = useRealm( ); - const queryClient = useQueryClient( ); - const currentUser = useCurrentUser( ); - const { t } = useTranslation( ); - - const { isConnected } = useNetInfo( ); - - const [showConfirm, setShowConfirm] = useState( false ); - const [showFeedback, setShowFeedback] = useState( false ); - - const drawerItemStyle = useMemo( ( ) => ( { - marginBottom: width <= BREAKPOINTS.lg - ? -15 - : -5 - } as const ), [] ); - - interface DrawerItem { - label: string; - navigation?: string; - icon: string; - color?: string; - style?: ViewStyle; - onPress?: ( ) => void; - testID?: string; - } - const drawerItems = useMemo( ( ) => { - const items: { - [key: string]: DrawerItem; - } = { - projects: { - label: t( "PROJECTS" ), - navigation: "Projects", - icon: "briefcase" - }, - about: { - label: t( "ABOUT" ), - navigation: "About", - icon: "inaturalist" - }, - donate: { - label: t( "DONATE" ), - navigation: "Donate", - icon: "heart" - }, - help: { - label: t( "HELP" ), - navigation: "Help", - icon: "help-circle" - }, - settings: { - testID: "settings", - label: t( "SETTINGS" ), - navigation: "Settings", - icon: "gear" - } - }; - items.feedback = { - label: t( "FEEDBACK" ), - icon: "feedback", - onPress: ( ) => { - if ( isConnected ) { - setShowFeedback( true ); - } else { - showOfflineAlert( t ); - } - } - }; - - if ( currentUser ) { - items.logout = { - label: t( "LOG-OUT" ), - icon: "door-exit", - style: { - opacity: 0.5, - display: "flex" - }, - onPress: ( ) => setShowConfirm( true ) - }; - } else { - items.login = { - label: t( "LOG-IN" ), - icon: "door-enter", - color: colors.inatGreen, - style: { - display: "flex" - }, - onPress: ( ) => { - navigation.navigate( "LoginStackNavigator" ); - } - }; - } - if ( isDebug ) { - items.debug = { - label: "DEBUG", - navigation: "Debug", - icon: "triangle-exclamation", - color: "deeppink" - }; - } - return items; - }, [currentUser, isConnected, isDebug, navigation, t] ); - - const onSignOut = async ( ) => { - await signOut( { realm, clearRealm: true, queryClient } ); - setShowConfirm( false ); - - // TODO might be necessary to restart the app at this point. We just - // deleted the realm file on disk, but the RealmProvider may still have a - // copy of realm in local state - navigation.goBack( ); - }; - - const renderIcon = useCallback( ( key: string ) => ( - - ), [drawerItems, isDarkMode] ); - - const renderLabel = useCallback( ( label: string ) => ( - - {label} - - ), [isDarkMode] ); - - const renderTopBanner = useCallback( ( ) => ( - { - if ( !currentUser ) { - navigation.navigate( "LoginStackNavigator" ); - } else { - navigation.navigate( "TabNavigator", { - screen: "ObservationsTab", - params: { - screen: "UserProfile", - params: { userId: currentUser.id } - } - } ); - } - }} - > - {currentUser - ? ( - - ) - : ( - - ) } - - - {currentUser - ? currentUser?.login - : t( "Log-in-to-iNaturalist" )} - - {currentUser && ( - - {t( "X-Observations", { count: currentUser.observations_count } )} - - )} - - - ), [currentUser, navigation, t, isDarkMode] ); - - const renderDrawerItem = useCallback( ( key: string ) => ( - - renderIcon( key )} - label={() => renderLabel( drawerItems[key].label )} - onPress={( ) => { - if ( drawerItems[key].navigation ) { - navigation.navigate( "TabNavigator", { - screen: "ObservationsTab", - params: { - screen: drawerItems[key].navigation - } - } ); - } - if ( drawerItems[key].onPress ) { - drawerItems[key].onPress(); - } - }} - style={[drawerItemStyle, drawerItems[key].style]} - /> - - ), [ - drawerItemStyle, - renderLabel, - renderIcon, - drawerItems, - navigation - ] ); - - const submitFeedback = useCallback( ( text: string ) => { - if ( !isConnected ) { - showOfflineAlert( t ); - return false; - } - const mode = isDefaultMode( ) - ? "DEFAULT:" - : "ADVANCED:"; - feedbackLogger.info( mode, text ); - Alert.alert( t( "Feedback-Submitted" ), t( "Thank-you-for-sharing-your-feedback" ) ); - setShowFeedback( false ); - return true; - }, [isConnected, t] ); - - return ( - - - {renderTopBanner( )} - - {Object.keys( drawerItems ).map( item => renderDrawerItem( item ) )} - - - {showConfirm && ( - setShowConfirm( false )} - headerText={t( "LOG-OUT--question" )} - text={t( "Are-you-sure-you-want-to-log-out" )} - handleSecondButtonPress={() => setShowConfirm( false )} - secondButtonText={t( "CANCEL" )} - confirm={onSignOut} - buttonText={t( "LOG-OUT" )} - /> - )} - {showFeedback && ( - - ); -}; - -export default CustomDrawerContent; diff --git a/src/navigation/RootStackNavigator.tsx b/src/navigation/RootStackNavigator.tsx new file mode 100644 index 000000000..434fc7254 --- /dev/null +++ b/src/navigation/RootStackNavigator.tsx @@ -0,0 +1,47 @@ +import { createNativeStackNavigator } from "@react-navigation/native-stack"; +import LoginStackNavigator from "navigation/StackNavigators/LoginStackNavigator"; +import NoBottomTabStackNavigator from "navigation/StackNavigators/NoBottomTabStackNavigator"; +import OnboardingStackNavigator from "navigation/StackNavigators/OnboardingStackNavigator"; +import * as React from "react"; +import { useOnboardingShown } from "sharedHelpers/installData"; + +import BottomTabNavigator from "./BottomTabNavigator"; + +const Stack = createNativeStackNavigator( ); + +// DEVELOPERS: do you need to add any screens here? This is the RootStack. +// All the rest of our screens live in: +// NoBottomTabStackNavigator, TabStackNavigator, OnboardingStackNavigator, or LoginStackNavigator + +const RootStackNavigator = ( ) => { + const [onboardingShown] = useOnboardingShown( ); + + return ( + + {!onboardingShown + ? ( + + ) + : ( + + + )} + + + + ); +}; + +export default RootStackNavigator; diff --git a/src/navigation/rootDrawerNavigator.js b/src/navigation/rootDrawerNavigator.js deleted file mode 100644 index 36ce11e88..000000000 --- a/src/navigation/rootDrawerNavigator.js +++ /dev/null @@ -1,80 +0,0 @@ -// @flow - -import { createDrawerNavigator } from "@react-navigation/drawer"; -import { - hideDrawerHeaderLeft, hideHeader -} from "navigation/navigationOptions"; -import LoginStackNavigator from "navigation/StackNavigators/LoginStackNavigator"; -import NoBottomTabStackNavigator from "navigation/StackNavigators/NoBottomTabStackNavigator"; -import OnboardingStackNavigator from "navigation/StackNavigators/OnboardingStackNavigator"; -import type { Node } from "react"; -import * as React from "react"; -import { useColorScheme } from "react-native"; -import { useOnboardingShown } from "sharedHelpers/installData"; - -import BottomTabNavigator from "./BottomTabNavigator"; -import CustomDrawerContent from "./CustomDrawerContent"; - -const drawerOptions = { - ...hideHeader, - ...hideDrawerHeaderLeft, - drawerType: "front", - drawerStyle: { - backgroundColor: "transparent" - }, - swipeEnabled: false -}; - -const Drawer = createDrawerNavigator( ); - -// DEVELOPERS: do you need to add any screens here? All the rest of our screens live in -// NoBottomTabStackNavigator, TabStackNavigator, OnboardingStackNavigator, or LoginStackNavigator - -const RootDrawerNavigator = ( ): Node => { - const [onboardingShown] = useOnboardingShown( ); - const colorScheme = useColorScheme( ); - - const drawerRenderer = ( { - state, navigation, descriptors - } ) => ( - - ); - - return ( - - {!onboardingShown - ? ( - - ) - : ( - - - )} - - - - ); -}; - -export default RootDrawerNavigator;