restructure root nav and delete dead drawer code and library

This commit is contained in:
Abbey Campbell
2025-12-04 10:45:41 -08:00
parent caca94fb48
commit e8ee6c39cb
6 changed files with 49 additions and 484 deletions

53
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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 => {
<StartupService />
<NetworkService />
<AppStateListener />
{children || <RootDrawerNavigator />}
{children || <RootStackNavigator />}
</>
);
};

View File

@@ -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 ) => (
<INatIcon
name={drawerItems[key].icon}
size={22}
color={isDarkMode
? colors.white
: drawerItems[key].color}
/>
), [drawerItems, isDarkMode] );
const renderLabel = useCallback( ( label: string ) => (
<Heading4 className={classnames(
isDarkMode && "dark:text-white"
)}
>
{label}
</Heading4>
), [isDarkMode] );
const renderTopBanner = useCallback( ( ) => (
<Pressable
testID="drawer-top-banner"
accessibilityRole="button"
className={classnames(
currentUser
? "ml-5"
: "ml-3",
"mb-5",
"flex-row",
"flex-nowrap",
"mr-3"
)}
onPress={( ) => {
if ( !currentUser ) {
navigation.navigate( "LoginStackNavigator" );
} else {
navigation.navigate( "TabNavigator", {
screen: "ObservationsTab",
params: {
screen: "UserProfile",
params: { userId: currentUser.id }
}
} );
}
}}
>
{currentUser
? (
<UserIcon
uri={User.uri( currentUser )}
/>
)
: (
<INatIconButton
icon="inaturalist"
size={40}
color={colors.inatGreen}
accessibilityLabel="iNaturalist"
accessibilityHint={t( "Shows-iNaturalist-bird-logo" )}
/>
) }
<View className="ml-3 justify-center">
<Body1 className={classnames(
isDarkMode && "dark:text-white"
)}
>
{currentUser
? currentUser?.login
: t( "Log-in-to-iNaturalist" )}
</Body1>
{currentUser && (
<List2>
{t( "X-Observations", { count: currentUser.observations_count } )}
</List2>
)}
</View>
</Pressable>
), [currentUser, navigation, t, isDarkMode] );
const renderDrawerItem = useCallback( ( key: string ) => (
<View
className="mb-6"
key={drawerItems[key].label}
>
<DrawerItem
testID={drawerItems[key].testID}
accessibilityLabel={drawerItems[key].label}
icon={( ) => 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]}
/>
</View>
), [
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 (
<DrawerContentScrollView
state={state}
navigation={navigation}
descriptors={descriptors}
contentContainerStyle={drawerScrollViewStyle}
>
<View className="py-5 flex">
{renderTopBanner( )}
<View className="ml-3">
{Object.keys( drawerItems ).map( item => renderDrawerItem( item ) )}
</View>
</View>
{showConfirm && (
<WarningSheet
onPressClose={() => 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 && (
<TextInputSheet
hidden={!showFeedback}
buttonText={t( "SUBMIT" )}
onPressClose={() => setShowFeedback( false )}
headerText={t( "FEEDBACK" )}
confirm={submitFeedback}
description={t( "Thanks-for-using-any-suggestions" )}
maxLength={1000}
/>
)}
</DrawerContentScrollView>
);
};
export default CustomDrawerContent;

View File

@@ -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 (
<Stack.Navigator screenOptions={{ headerShown: false }}>
{!onboardingShown
? (
<Stack.Screen
name="OnboardingStackNavigator"
component={OnboardingStackNavigator}
/>
)
: (
<Stack.Screen
name="TabNavigator"
component={BottomTabNavigator}
/>
)}
<Stack.Screen
name="NoBottomTabStackNavigator"
component={NoBottomTabStackNavigator}
/>
<Stack.Screen
name="LoginStackNavigator"
component={LoginStackNavigator}
/>
</Stack.Navigator>
);
};
export default RootStackNavigator;

View File

@@ -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
} ) => (
<CustomDrawerContent
state={state}
navigation={navigation}
descriptors={descriptors}
colorScheme={colorScheme}
/>
);
return (
<Drawer.Navigator
screenOptions={drawerOptions}
name="Drawer"
drawerContent={drawerRenderer}
>
{!onboardingShown
? (
<Drawer.Screen
name="OnboardingStackNavigator"
component={OnboardingStackNavigator}
/>
)
: (
<Drawer.Screen
name="TabNavigator"
component={BottomTabNavigator}
/>
)}
<Drawer.Screen
name="NoBottomTabStackNavigator"
component={NoBottomTabStackNavigator}
/>
<Drawer.Screen
name="LoginStackNavigator"
component={LoginStackNavigator}
/>
</Drawer.Navigator>
);
};
export default RootDrawerNavigator;