mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2025-12-23 22:18:36 -05:00
restructure root nav and delete dead drawer code and library
This commit is contained in:
53
package-lock.json
generated
53
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
47
src/navigation/RootStackNavigator.tsx
Normal file
47
src/navigation/RootStackNavigator.tsx
Normal 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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user