Skip to content

Commit 005fd93

Browse files
committed
feat: hot drawer
1 parent 9d83e11 commit 005fd93

File tree

1 file changed

+132
-103
lines changed

1 file changed

+132
-103
lines changed

src/navigation/Navigator.tsx

Lines changed: 132 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,44 @@
22
* Created by leon<[email protected]> on 22/2/21.
33
*/
44
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
5+
import { createDrawerNavigator, DrawerContentComponentProps } from '@react-navigation/drawer'
56
import {
7+
DefaultTheme,
68
getFocusedRouteNameFromRoute,
79
NavigationContainer,
810
NavigationContainerRefWithCurrent,
11+
NavigationProp,
912
NavigationState,
1013
PartialState,
11-
DefaultTheme,
12-
Route,
13-
RouteProp,
14-
NavigationProp
14+
Route
1515
} from '@react-navigation/native'
1616
import { createNativeStackNavigator, NativeStackNavigationOptions } from '@react-navigation/native-stack'
17-
import {
18-
createDrawerNavigator,
19-
DrawerNavigationProp,
20-
getDrawerStatusFromState,
21-
useDrawerStatus
22-
} from '@react-navigation/drawer'
17+
import { Text } from '@src/components'
2318
import { ToastProvider } from '@src/components/toast'
2419
import { useAppSelector } from '@src/hooks'
2520
import { useUnRead } from '@src/hooks/useUnRead'
2621
import { changeLocale, LanguageTagType, translate } from '@src/i18n'
2722
import * as Screens from '@src/screens'
23+
import { HeaderButton } from '@src/screens/components'
2824
import { RootState, store } from '@src/store'
2925
import { ITheme, useTheme } from '@src/theme'
3026
import { wait } from '@src/utils/utils'
3127
import dayjs from 'dayjs'
3228
import enUS from 'dayjs/locale/en'
3329
import zhCN from 'dayjs/locale/zh-cn'
3430
import relativeTime from 'dayjs/plugin/relativeTime'
35-
import React, { ReactNode, useEffect, useState } from 'react'
36-
import { Image, Platform, StatusBar, TextStyle, View } from 'react-native'
31+
import React, { ReactNode, useCallback, useEffect, useState } from 'react'
32+
import { Image, Platform, StatusBar, TextStyle, TouchableOpacity, View, ViewStyle } from 'react-native'
3733
import { EdgeInsets, SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
3834
import SplashScreen from 'react-native-splash-screen'
3935
import NavigationService from './NavigationService'
40-
import { MainScreenProps, RootStackParamList, ROUTES } from './routes'
41-
import { HeaderButton } from '@src/screens/components'
36+
import { CommonScreenProps, MainScreenProps, RootStackParamList, ROUTES } from './routes'
4237

4338
/**
4439
* dayjs
4540
*/
4641
dayjs.extend(relativeTime)
4742

48-
const MainBottomTabNavigator = createBottomTabNavigator()
49-
const bottomTabBarIconSize = 30
50-
51-
/**
52-
* Crate Drawer Navigator
53-
*/
54-
const DrawerNavigator = createDrawerNavigator()
55-
5643
/**
5744
* header background default style
5845
* @param borderWidth
@@ -72,31 +59,37 @@ const defaultHeaderBackground = (theme: ITheme, borderWidth?: number): ReactNode
7259
)
7360
}
7461

75-
const defaultScreenOptions = (theme: ITheme): NativeStackNavigationOptions => ({
76-
animationTypeForReplace: 'push',
77-
animation: 'slide_from_right',
78-
62+
const defaultCommonScreenOptions = (theme: ITheme) => ({
7963
// hide header shadow
8064
headerShadowVisible: false,
8165

8266
headerStyle: {
8367
backgroundColor: theme.colors.transparent
8468
},
85-
headerTitleStyle: {
86-
fontWeight: 'bold',
87-
fontSize: theme.typography.titleText.fontSize
88-
},
8969
headerBackground: () => defaultHeaderBackground(theme),
90-
headerBackTitle: undefined,
9170
headerTintColor: theme.colors.appbarTint,
92-
headerBackTitleVisible: false,
9371

9472
// screen main content style
9573
contentStyle: {
9674
backgroundColor: theme.colors.background
9775
}
9876
})
9977

78+
const defaultScreenOptions = (theme: ITheme): NativeStackNavigationOptions => ({
79+
...defaultCommonScreenOptions(theme),
80+
81+
animationTypeForReplace: 'push',
82+
animation: 'slide_from_right',
83+
84+
headerTitleStyle: {
85+
fontWeight: 'bold',
86+
fontSize: theme.typography.titleText.fontSize
87+
},
88+
89+
headerBackTitle: undefined,
90+
headerBackTitleVisible: false
91+
})
92+
10093
const resetLocales = (locale: LanguageTagType) => {
10194
changeLocale(locale)
10295
dayjs.locale(locale === 'zh' ? zhCN : enUS)
@@ -111,7 +104,7 @@ const badgeStyles = {
111104
})
112105
}
113106

114-
const getHeaderTitle = (
107+
const getDrawHeaderTitle = (
115108
route: Partial<Route<string>> & {
116109
state?: PartialState<NavigationState>
117110
}
@@ -121,32 +114,23 @@ const getHeaderTitle = (
121114
// In our case, it's "Feed" as that's the first screen inside the navigator
122115
const routeName = getFocusedRouteNameFromRoute(route) ?? ROUTES.Hot
123116
switch (routeName) {
124-
case ROUTES.HotDraw:
125-
return translate(`router.${ROUTES.Hot}`)
126117
case ROUTES.Hot:
127118
return translate(`router.${ROUTES.Hot}`)
128119
case ROUTES.Latest:
129120
return translate(`router.${ROUTES.Latest}`)
130-
case ROUTES.Nodes:
131-
return translate(`router.${ROUTES.Nodes}`)
132-
case ROUTES.Notifications:
133-
return translate(`router.${ROUTES.Notifications}`)
134-
case ROUTES.InterestNodes:
135-
return translate(`router.${ROUTES.InterestNodes}`)
136-
case ROUTES.My:
137-
return translate(`router.${ROUTES.My}`)
138121
}
139122
}
140123

124+
const bottomTabBarIconSize = 30
141125
const renderBottomIcon = (focused: boolean, activeIcon: any, inactiveIcon: any): Element => {
142126
const icon = focused ? activeIcon : inactiveIcon
143127
return <Image source={icon} style={{ width: bottomTabBarIconSize, height: bottomTabBarIconSize }} />
144128
}
145129

146130
const defaultTabBarSetting = (theme: ITheme, insets: EdgeInsets) => {
147131
return {
148-
...defaultScreenOptions,
149-
headerShown: false,
132+
...defaultCommonScreenOptions(theme),
133+
headerShown: true,
150134
tabBarActiveTintColor: theme.colors.tabBarIconActive,
151135
tabBarInactiveTintColor: theme.colors.tabBarIconInactive,
152136
tabBarShowLabel: false,
@@ -162,73 +146,121 @@ const defaultTabBarSetting = (theme: ITheme, insets: EdgeInsets) => {
162146
}
163147
}
164148

165-
const HotDrawerNavigator = (initialRouteName?: string) => {
149+
const HotDrawerContent = (props: DrawerContentComponentProps) => {
150+
const { theme } = useTheme()
151+
const insets = useSafeAreaInsets()
152+
153+
const isFocus = useCallback(
154+
(route_index: number) => {
155+
return props.state.index === route_index
156+
},
157+
[theme, props]
158+
)
159+
160+
const textStyle = useCallback(
161+
(route_index: number) => {
162+
return {
163+
color: isFocus(route_index) ? theme.colors.secondary : theme.colors.captionText
164+
}
165+
},
166+
[theme, props]
167+
)
168+
return (
169+
<View style={[drawContentStyles.drawerContent(theme), { paddingTop: insets.top }]}>
170+
<TouchableOpacity
171+
style={drawContentStyles.drawItem(theme)}
172+
onPress={() => {
173+
NavigationService.navigate(ROUTES.Hot)
174+
}}>
175+
<Image source={theme.assets.images.icons.draw.hot[isFocus(0) ? 'active' : 'inActive']} />
176+
<Text type="label" style={textStyle(0)}>
177+
{translate(`router.${ROUTES.Hot}`)}
178+
</Text>
179+
</TouchableOpacity>
180+
<TouchableOpacity
181+
style={drawContentStyles.drawItem(theme)}
182+
onPress={() => {
183+
NavigationService.navigate(ROUTES.Latest)
184+
}}>
185+
<Image source={theme.assets.images.icons.draw.latest[isFocus(1) ? 'active' : 'inActive']} />
186+
<Text type="label" style={textStyle(1)}>
187+
{translate(`router.${ROUTES.Latest}`)}
188+
</Text>
189+
</TouchableOpacity>
190+
</View>
191+
)
192+
}
193+
194+
const drawContentStyles = {
195+
drawerContent: (theme: ITheme): ViewStyle => ({
196+
flexDirection: 'column',
197+
justifyContent: 'flex-start',
198+
alignItems: 'center',
199+
flex: 1,
200+
backgroundColor: theme.colors.background
201+
}),
202+
drawItem: (theme: ITheme): ViewStyle => ({
203+
marginBottom: theme.spacing.medium
204+
})
205+
}
206+
207+
/**
208+
* Crate Drawer Navigator
209+
*/
210+
const HotDraw = createDrawerNavigator()
211+
const HotDrawerNavigator = ({
212+
initialRouteName,
213+
navigation
214+
}: {
215+
initialRouteName?: string
216+
navigation: NavigationProp<RootStackParamList>
217+
} & CommonScreenProps) => {
166218
initialRouteName = initialRouteName ?? ROUTES.Hot
219+
const { theme } = useTheme()
167220

168221
return (
169-
<DrawerNavigator.Navigator initialRouteName={initialRouteName}>
170-
<DrawerNavigator.Screen
222+
<HotDraw.Navigator
223+
initialRouteName={initialRouteName}
224+
drawerContent={(props) => <HotDrawerContent {...props} />}
225+
screenOptions={{
226+
headerRight: () => (
227+
<HeaderButton
228+
onPress={() => navigation.navigate(ROUTES.SiteStat)}
229+
source={theme.assets.images.icons.header.stat}
230+
containerStyle={[{ marginRight: theme.spacing.medium }]}
231+
/>
232+
),
233+
drawerActiveTintColor: theme.colors.secondary,
234+
drawerActiveBackgroundColor: theme.colors.secondary,
235+
drawerInactiveTintColor: theme.colors.captionText,
236+
drawerStyle: { width: 45 }
237+
}}>
238+
<HotDraw.Screen
239+
key={ROUTES.Hot}
171240
name={ROUTES.Hot}
172241
component={Screens.HotScreen}
173242
options={{
174-
...defaultScreenOptions,
175-
headerShown: false,
243+
...defaultCommonScreenOptions(theme),
244+
headerShown: true,
176245
title: translate(`router.${ROUTES.Hot}`)
177246
}}
178247
/>
179-
<DrawerNavigator.Screen
248+
<HotDraw.Screen
249+
key={ROUTES.Latest}
180250
name={ROUTES.Latest}
181251
component={Screens.LatestScreen}
182252
options={{
183-
...defaultScreenOptions,
184-
headerShown: false,
253+
...defaultCommonScreenOptions(theme),
254+
headerShown: true,
185255
title: translate(`router.${ROUTES.Latest}`)
186256
}}
187257
/>
188-
</DrawerNavigator.Navigator>
258+
</HotDraw.Navigator>
189259
)
190260
}
191261

192-
const MainBottomHeaderLeft = ({
193-
navigation,
194-
route
195-
}: {
196-
route: RouteProp<RootStackParamList>
197-
navigation: NavigationProp<RootStackParamList>
198-
}): ReactNode => {
199-
const { theme } = useTheme()
200-
const focusRouteName = getFocusedRouteNameFromRoute(route) ?? ROUTES.HotDraw
201-
return focusRouteName === ROUTES.HotDraw ? (
202-
<HeaderButton
203-
source={theme.assets.images.icons.header.more}
204-
onPress={() => {
205-
console.log(getFocusedRouteNameFromRoute(route))
206-
}}
207-
/>
208-
) : undefined
209-
}
210-
211-
const MainBottomTabHeaderRight = ({
212-
navigation,
213-
route
214-
}: {
215-
route: RouteProp<RootStackParamList>
216-
navigation: NavigationProp<RootStackParamList>
217-
}): ReactNode => {
218-
const { theme } = useTheme()
219-
220-
const focusRouteName = getFocusedRouteNameFromRoute(route) ?? ROUTES.HotDraw
221-
return focusRouteName === ROUTES.HotDraw ? (
222-
<HeaderButton
223-
source={theme.assets.images.icons.header.stat}
224-
onPress={() => {
225-
NavigationService.navigate(ROUTES.SiteStat)
226-
}}
227-
/>
228-
) : undefined
229-
}
230-
231-
const MainAppNavigator = ({ navigation, route }: MainScreenProps) => {
262+
const MainBottomTabNavigator = createBottomTabNavigator()
263+
const MainAppNavigator = () => {
232264
const insets = useSafeAreaInsets()
233265
const { unread } = useUnRead()
234266
const { languageTag } = useAppSelector((state: RootState) => state.setting)
@@ -243,16 +275,17 @@ const MainAppNavigator = ({ navigation, route }: MainScreenProps) => {
243275
<MainBottomTabNavigator.Screen
244276
name={ROUTES.HotDraw}
245277
component={HotDrawerNavigator}
246-
options={{
247-
title: translate(`router.${ROUTES.HotDraw}`),
278+
options={({ route }) => ({
279+
title: getDrawHeaderTitle(route),
248280
...defaultTabBarSetting(theme, insets),
281+
headerShown: false,
249282
tabBarIcon: ({ focused }) =>
250283
renderBottomIcon(
251284
focused,
252285
theme.assets.images.icons.bottomTab.hot.active,
253286
theme.assets.images.icons.bottomTab.hot.inActive
254287
)
255-
}}
288+
})}
256289
/>
257290
<MainBottomTabNavigator.Screen
258291
name={ROUTES.Nodes}
@@ -384,12 +417,8 @@ export const AppNavigationContainer = () => {
384417
component={MainAppNavigator}
385418
options={({ route, navigation }) => ({
386419
...defaultScreenOptions(theme),
387-
headerShadowVisible: ![ROUTES.HotDraw].includes(
388-
getFocusedRouteNameFromRoute(route) ?? (ROUTES.Nodes as any)
389-
),
390-
headerLeft: () => MainBottomHeaderLeft({ route, navigation }),
391-
headerRight: () => MainBottomTabHeaderRight({ route, navigation }),
392-
headerTitle: getHeaderTitle(route)
420+
headerBackground: undefined,
421+
headerShown: false
393422
})}
394423
initialParams={{
395424
initialRouteName: ROUTES.My

0 commit comments

Comments
 (0)