Blame · Line-by-line history
MainTabNavigator.tsx
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| c53cf00 | 1 | import React from 'react'; |
| 2 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; | |
| 3 | import { createNativeStackNavigator } from '@react-navigation/native-stack'; | |
| 4 | import { Text, View } from 'react-native'; | |
| 5 | import { colors } from '../theme/colors'; | |
| 6 | import { fontSizes } from '../theme/typography'; | |
| 7 | ||
| 8 | import { DashboardScreen } from '../screens/DashboardScreen'; | |
| 9 | import { RepoListScreen } from '../screens/RepoListScreen'; | |
| 10 | import { RepoDetailScreen } from '../screens/RepoDetailScreen'; | |
| 11 | import { FileViewerScreen } from '../screens/FileViewerScreen'; | |
| 12 | import { IssueListScreen } from '../screens/IssueListScreen'; | |
| 13 | import { IssueDetailScreen } from '../screens/IssueDetailScreen'; | |
| 14 | import { PullListScreen } from '../screens/PullListScreen'; | |
| 15 | import { PullDetailScreen } from '../screens/PullDetailScreen'; | |
| 16 | import { NotificationsScreen } from '../screens/NotificationsScreen'; | |
| 17 | import { SettingsScreen } from '../screens/SettingsScreen'; | |
| 18 | ||
| 19 | // ─── Stack param types ─────────────────────────────────────────────────────── | |
| 20 | ||
| 21 | export type MainStackParamList = { | |
| 22 | Dashboard: undefined; | |
| 23 | RepoList: undefined; | |
| 24 | RepoDetail: { owner: string; repo: string }; | |
| 25 | FileViewer: { owner: string; repo: string; path: string; ref: string }; | |
| 26 | IssueList: { owner: string; repo: string }; | |
| 27 | IssueDetail: { owner: string; repo: string; number: number }; | |
| 28 | PullList: { owner: string; repo: string }; | |
| 29 | PullDetail: { owner: string; repo: string; number: number }; | |
| 30 | }; | |
| 31 | ||
| 32 | const Stack = createNativeStackNavigator<MainStackParamList>(); | |
| 33 | const Tab = createBottomTabNavigator(); | |
| 34 | ||
| 35 | // ─── Stack navigators (one per tab root) ──────────────────────────────────── | |
| 36 | ||
| 37 | function DashboardStack() { | |
| 38 | return ( | |
| 39 | <Stack.Navigator screenOptions={stackScreenOptions}> | |
| 40 | <Stack.Screen name="Dashboard" component={DashboardScreen} options={{ title: 'Dashboard' }} /> | |
| 41 | <Stack.Screen name="RepoDetail" component={RepoDetailScreen} options={({ route }) => ({ title: route.params.repo })} /> | |
| 42 | <Stack.Screen name="FileViewer" component={FileViewerScreen} options={({ route }) => ({ title: route.params.path.split('/').pop() ?? 'File' })} /> | |
| 43 | <Stack.Screen name="IssueList" component={IssueListScreen} options={{ title: 'Issues' }} /> | |
| 44 | <Stack.Screen name="IssueDetail" component={IssueDetailScreen} options={({ route }) => ({ title: `#${route.params.number}` })} /> | |
| 45 | <Stack.Screen name="PullList" component={PullListScreen} options={{ title: 'Pull Requests' }} /> | |
| 46 | <Stack.Screen name="PullDetail" component={PullDetailScreen} options={({ route }) => ({ title: `PR #${route.params.number}` })} /> | |
| 47 | {/* Dummy screens required by type — never actually navigated to from this stack */} | |
| 48 | <Stack.Screen name="RepoList" component={RepoListScreen} options={{ title: 'Repositories' }} /> | |
| 49 | </Stack.Navigator> | |
| 50 | ); | |
| 51 | } | |
| 52 | ||
| 53 | function RepoStack() { | |
| 54 | return ( | |
| 55 | <Stack.Navigator screenOptions={stackScreenOptions}> | |
| 56 | <Stack.Screen name="RepoList" component={RepoListScreen} options={{ title: 'Repositories' }} /> | |
| 57 | <Stack.Screen name="RepoDetail" component={RepoDetailScreen} options={({ route }) => ({ title: route.params.repo })} /> | |
| 58 | <Stack.Screen name="FileViewer" component={FileViewerScreen} options={({ route }) => ({ title: route.params.path.split('/').pop() ?? 'File' })} /> | |
| 59 | <Stack.Screen name="IssueList" component={IssueListScreen} options={{ title: 'Issues' }} /> | |
| 60 | <Stack.Screen name="IssueDetail" component={IssueDetailScreen} options={({ route }) => ({ title: `#${route.params.number}` })} /> | |
| 61 | <Stack.Screen name="PullList" component={PullListScreen} options={{ title: 'Pull Requests' }} /> | |
| 62 | <Stack.Screen name="PullDetail" component={PullDetailScreen} options={({ route }) => ({ title: `PR #${route.params.number}` })} /> | |
| 63 | {/* Dummy — needed to satisfy shared type */} | |
| 64 | <Stack.Screen name="Dashboard" component={DashboardScreen} options={{ title: 'Dashboard' }} /> | |
| 65 | </Stack.Navigator> | |
| 66 | ); | |
| 67 | } | |
| 68 | ||
| 69 | // ─── Tab navigator ─────────────────────────────────────────────────────────── | |
| 70 | ||
| 71 | export function MainTabNavigator() { | |
| 72 | return ( | |
| 73 | <Tab.Navigator | |
| 74 | screenOptions={{ | |
| 75 | headerShown: false, | |
| 76 | tabBarStyle: { | |
| 77 | backgroundColor: colors.bgSecondary, | |
| 78 | borderTopColor: colors.border, | |
| 79 | borderTopWidth: 1, | |
| 80 | height: 60, | |
| 81 | paddingBottom: 8, | |
| 82 | }, | |
| 83 | tabBarActiveTintColor: colors.accent, | |
| 84 | tabBarInactiveTintColor: colors.textMuted, | |
| 85 | tabBarLabelStyle: { | |
| 86 | fontSize: fontSizes.xs, | |
| 87 | fontWeight: '500', | |
| 88 | }, | |
| 89 | }} | |
| 90 | > | |
| 91 | <Tab.Screen | |
| 92 | name="DashboardTab" | |
| 93 | component={DashboardStack} | |
| 94 | options={{ | |
| 95 | title: 'Dashboard', | |
| 96 | tabBarIcon: ({ color }) => <TabIcon icon="⌂" color={color} />, | |
| 97 | }} | |
| 98 | /> | |
| 99 | <Tab.Screen | |
| 100 | name="ReposTab" | |
| 101 | component={RepoStack} | |
| 102 | options={{ | |
| 103 | title: 'Repos', | |
| 104 | tabBarIcon: ({ color }) => <TabIcon icon="⌥" color={color} />, | |
| 105 | }} | |
| 106 | /> | |
| 107 | <Tab.Screen | |
| 108 | name="NotificationsTab" | |
| 109 | component={NotificationsScreen} | |
| 110 | options={{ | |
| 111 | title: 'Notifications', | |
| 112 | tabBarIcon: ({ color }) => <TabIcon icon="◉" color={color} />, | |
| 113 | }} | |
| 114 | /> | |
| 115 | <Tab.Screen | |
| 116 | name="SettingsTab" | |
| 117 | component={SettingsScreen} | |
| 118 | options={{ | |
| 119 | title: 'Settings', | |
| 120 | tabBarIcon: ({ color }) => <TabIcon icon="⚙" color={color} />, | |
| 121 | }} | |
| 122 | /> | |
| 123 | </Tab.Navigator> | |
| 124 | ); | |
| 125 | } | |
| 126 | ||
| 127 | // ─── Helpers ───────────────────────────────────────────────────────────────── | |
| 128 | ||
| 129 | function TabIcon({ icon, color }: { icon: string; color: string }) { | |
| 130 | return ( | |
| 131 | <Text style={{ fontSize: 18, color, lineHeight: 22 }}>{icon}</Text> | |
| 132 | ); | |
| 133 | } | |
| 134 | ||
| 135 | const stackScreenOptions = { | |
| 136 | headerStyle: { backgroundColor: colors.bgSecondary }, | |
| 137 | headerTintColor: colors.text, | |
| 138 | headerTitleStyle: { color: colors.text, fontWeight: '600' as const }, | |
| 139 | headerBackTitleVisible: false, | |
| 140 | contentStyle: { backgroundColor: colors.bg }, | |
| 141 | }; |