289 lines
7.8 KiB
TypeScript
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>
|
|
);
|
|
}
|