pottery-diary/src/navigation/index.tsx

289 lines
7.8 KiB
TypeScript

import React from 'react';
import { Text, View, StyleSheet, TouchableOpacity, ActivityIndicator, Image } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
import { RootStackParamList, MainTabParamList } from './types';
import {
LoginScreen,
SignUpScreen,
OnboardingScreen,
HomeScreen,
ProjectsScreen,
ProjectDetailScreen,
StepEditorScreen,
NewsScreen,
SettingsScreen,
GlazePickerScreen,
UserListScreen,
} from '../screens';
import { colors, spacing } from '../lib/theme';
import { useAuth } from '../contexts/AuthContext';
const RootStack = createNativeStackNavigator<RootStackParamList>();
const MainTab = createBottomTabNavigator<MainTabParamList>();
const homeIcon = require('../../assets/images/home_icon.png');
const projectIcon = require('../../assets/images/project_icon.png');
const tipsIcon = require('../../assets/images/tips_icon.png');
const settingsIcon = require('../../assets/images/settings_icon.png');
function CustomTabBar({ state, descriptors, navigation }: BottomTabBarProps) {
const tabs = [
{ name: 'Home', label: 'Home', iconType: 'image' as const, iconSource: homeIcon },
{ name: 'Projects', label: 'Projects', iconType: 'image' as const, iconSource: projectIcon },
{ name: 'News', label: 'Tips', iconType: 'image' as const, iconSource: tipsIcon },
{ name: 'Settings', label: 'Settings', iconType: 'image' as const, iconSource: settingsIcon },
];
return (
<View style={styles.tabBarContainer}>
{tabs.map((tab, index) => {
const isFocused = state.index === index;
const isFirst = index === 0;
const isLast = index === tabs.length - 1;
const isMiddle = !isFirst && !isLast;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: state.routes[index].key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(state.routes[index].name);
}
};
return (
<TouchableOpacity
key={tab.name}
onPress={onPress}
style={[
styles.tabItem,
isFocused && styles.tabItemActive,
isFocused && isFirst && styles.tabItemActiveFirst,
isFocused && isLast && styles.tabItemActiveLast,
isFocused && isMiddle && styles.tabItemActiveMiddle,
]}
>
<View style={styles.imageIconContainer}>
<Image
source={tab.iconSource}
style={[
styles.tabIconImage,
isFocused && styles.tabIconImageActive
]}
resizeMode="contain"
/>
</View>
<Text style={[
styles.tabLabel,
isFocused && styles.tabLabelActive
]}>
{tab.label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
function MainTabs() {
return (
<MainTab.Navigator
tabBar={props => <CustomTabBar {...props} />}
screenOptions={{
headerShown: false,
}}
initialRouteName="Home"
>
<MainTab.Screen
name="Home"
component={HomeScreen}
options={{
tabBarLabel: 'Home',
}}
/>
<MainTab.Screen
name="Projects"
component={ProjectsScreen}
options={{
tabBarLabel: 'Projects',
}}
/>
<MainTab.Screen
name="News"
component={NewsScreen}
options={{
tabBarLabel: 'Tips',
}}
/>
<MainTab.Screen
name="Settings"
component={SettingsScreen}
options={{
tabBarLabel: 'Settings',
}}
/>
</MainTab.Navigator>
);
}
const styles = StyleSheet.create({
tabBarContainer: {
flexDirection: 'row',
backgroundColor: colors.background,
height: 65,
position: 'absolute',
bottom: 20,
left: 10,
right: 10,
borderRadius: 25,
shadowColor: colors.text,
shadowOffset: { width: 0, height: -2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 8,
},
tabItem: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 8,
},
tabItemActive: {
backgroundColor: colors.primaryLight,
borderWidth: 2,
borderColor: colors.primary,
},
tabItemActiveFirst: {
borderRadius: 25,
},
tabItemActiveLast: {
borderRadius: 25,
},
tabItemActiveMiddle: {
borderRadius: 25,
},
imageIconContainer: {
width: 40,
height: 40,
borderRadius: 0,
overflow: 'hidden',
backgroundColor: 'transparent',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 2,
},
tabIconImage: {
width: 40,
height: 40,
opacity: 0.8,
backgroundColor: 'transparent',
},
tabIconImageActive: {
opacity: 1,
backgroundColor: 'transparent',
},
tabLabel: {
fontSize: 10,
fontWeight: '700',
letterSpacing: 0.3,
color: colors.textSecondary,
},
tabLabelActive: {
color: colors.text,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: colors.background,
},
});
export function AppNavigator() {
const { user, loading, hasCompletedOnboarding } = useAuth();
if (loading) {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={colors.primary} />
</View>
);
}
return (
<NavigationContainer>
<RootStack.Navigator
initialRouteName={!user ? 'Login' : hasCompletedOnboarding ? 'MainTabs' : 'Onboarding'}
screenOptions={{
headerStyle: {
backgroundColor: colors.background,
},
headerTintColor: colors.primary,
headerTitleStyle: {
fontWeight: '600',
},
}}
>
{!user ? (
// Auth screens - not logged in
<>
<RootStack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="SignUp"
component={SignUpScreen}
options={{ headerShown: false }}
/>
</>
) : (
// App screens - logged in (initial route determined by hasCompletedOnboarding)
<>
<RootStack.Screen
name="Onboarding"
component={OnboardingScreen}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="MainTabs"
component={MainTabs}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="ProjectDetail"
component={ProjectDetailScreen}
options={{ title: 'Project' }}
/>
<RootStack.Screen
name="StepEditor"
component={StepEditorScreen}
options={{ title: 'Add Step' }}
/>
<RootStack.Screen
name="GlazePicker"
component={GlazePickerScreen}
options={{ title: 'Select Glazes' }}
/>
<RootStack.Screen
name="UserList"
component={UserListScreen}
options={({ route }) => ({
title: route.params.type === 'shopping' ? 'Shopping List' : 'Wish List',
})}
/>
</>
)}
</RootStack.Navigator>
</NavigationContainer>
);
}