import Ionicons from "@expo/vector-icons/Ionicons"; import { DrawerContentComponentProps, DrawerContentScrollView, DrawerItem, } from "@react-navigation/drawer"; import { Href, useNavigation, useRouter, useSegments } from "expo-router"; import { Drawer } from "expo-router/drawer"; import { memo, useCallback, useLayoutEffect, useMemo, useState, } from "react"; import { Image, ImageSourcePropType, Pressable, StyleSheet, View, } from "react-native"; import { IoniconsType } from "../../lib/types"; export type CustomDrawerRoute = { name: string; href: string; icon?: IoniconsType; }; const styles = StyleSheet.create({ logo: { width: 107, height: 20, resizeMode: "contain", }, }); const DrawerHeader = memo( ({ onClose, LOGO_SOURCE, }: { onClose: () => void; LOGO_SOURCE: ImageSourcePropType; }) => ( ) ); const CustomDrawerItem = memo( ({ route, isActive, onPress, }: { route: CustomDrawerRoute; isActive: boolean; onPress: () => void; }) => ( ( )} label={route.name} focused={isActive} onPress={onPress} activeTintColor="#DC2626" activeBackgroundColor="#FEE2E2" inactiveTintColor="#6B7280" /> ) ); const StaticDrawerItem = memo( ({ name, icon, onPress, }: { name: string; href: string; icon: string; onPress: () => void; }) => ( ( )} label={name} onPress={onPress} /> ) ); const DrawerContent = memo( ({ state, navigation, FooterContent, CUSTOM_DRAWER_ROUTES, STATIC_ROUTES, LOGO_SOURCE, ...rest }: DrawerContentComponentProps & { CUSTOM_DRAWER_ROUTES: CustomDrawerRoute[]; STATIC_ROUTES: Required[]; FooterContent?: React.ReactNode; LOGO_SOURCE: ImageSourcePropType; }) => { const router = useRouter(); const segments = useSegments(); const focusedRoute = state.routes[state.index]; const focusedDescriptor = rest.descriptors[focusedRoute.key]; const focusedOptions = focusedDescriptor.options; const currentRoute = useMemo(() => { return "/" + segments.slice(1).join("/"); }, [segments]); const isRouteActive = useCallback( (href: string) => { if (currentRoute === href) return true; if (href !== "/(tabs)" && currentRoute.startsWith(href)) return true; if ( href === "/(tabs)/tags/list-tags" && currentRoute.includes("/tags/") ) return true; return false; }, [currentRoute] ); const handleCloseDrawer = useCallback(() => { navigation.closeDrawer(); }, [navigation]); const handleRoutePress = useCallback( (href: string) => { router.push(href as Href); }, [router] ); const { drawerContentStyle, drawerContentContainerStyle } = focusedOptions; return ( {CUSTOM_DRAWER_ROUTES.map((route) => ( handleRoutePress(route.href)} /> ))} {STATIC_ROUTES.map((route) => ( handleRoutePress(route.href)} /> ))} {FooterContent} ); } ); const useRouteTitle = ({ ALL_ROUTES, }: { ALL_ROUTES: CustomDrawerRoute[]; }) => { const segments: string[] = useSegments(); const navigation = useNavigation(); const [title, setTitle] = useState(ALL_ROUTES[0].name); const findRouteByHref = (href: string) => ALL_ROUTES.find((route) => route.href === href); useLayoutEffect(() => { const activeRoute = "/" + segments.slice(1).join("/"); let route = findRouteByHref(activeRoute); if (!route) { route = ALL_ROUTES.find((r) => activeRoute.startsWith(r.href.replace(/\/[^/]*$/, "")) ); } if (!route && segments.length >= 2) { const mainSection = `/(tabs)/${segments[1]}`; route = ALL_ROUTES.find((r) => r.href.startsWith(mainSection)); } if (route) { setTitle(route.name); navigation.setOptions({ headerTitle: route.name, }); } }, [segments, navigation]); return title; }; export function MainDrawer({ ALL_ROUTES, STATIC_ROUTES, CUSTOM_DRAWER_ROUTES, FooterContent, HeaderRight, LOGO_SOURCE, }: { ALL_ROUTES: CustomDrawerRoute[]; STATIC_ROUTES: Required[]; CUSTOM_DRAWER_ROUTES: CustomDrawerRoute[]; FooterContent?: React.ReactNode; HeaderRight?: React.ReactNode; LOGO_SOURCE: ImageSourcePropType; }) { const title = useRouteTitle({ ALL_ROUTES }); const drawerContent = useCallback( (props: DrawerContentComponentProps) => ( ), [CUSTOM_DRAWER_ROUTES] ); const screenOptions = useMemo( () => ({ headerStatusBarHeight: 0, drawerContentContainerStyle: { paddingTop: 20, flex: 1 }, headerTitleStyle: { fontSize: 16 }, headerStyle: { height: 50 }, headerRight: () => HeaderRight, }), [HeaderRight] ); const drawerScreenOptions = useMemo( () => ({ title, drawerLabel: "Home", }), [title] ); return ( ); }