You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

321 lines
8.4 KiB

import React, { useEffect, useState, useRef } from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Platform, Modal, View, StyleSheet, Text, TouchableOpacity, Animated, Keyboard } from 'react-native';
import Ionicons from '@expo/vector-icons/Ionicons';
import { useNavigation, useRoute } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { RootStackParamList } from '../types/navigation';
import { useAuth } from '../contexts/AuthContext';
import { useTranslation } from 'react-i18next';
import { HomeScreen } from '../screens/HomeScreen';
import { CategoryScreen } from '../screens/CategoryScreen';
import { ChatScreen } from '../screens/ChatScreen';
import { CartScreen } from '../screens/CartScreen';
import { ProfileScreen } from '../screens/ProfileScreen';
type IconProps = {
name: string;
size: number;
color: string;
};
const IconComponent = ({ name, size, color }: IconProps) => {
const Icon = Ionicons as any;
return <Icon name={name} size={size} color={color} />;
};
type TabBarIconProps = {
color: string;
size: number;
};
const Tab = createBottomTabNavigator();
export const TabNavigator = () => {
const { t } = useTranslation();
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
const route = useRoute();
const { isLoggedIn } = useAuth();
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
const [keyboardVisible, setKeyboardVisible] = useState(false);
// 动画值
const fadeAnim = useRef(new Animated.Value(0)).current;
const slideAnim = useRef(new Animated.Value(300)).current;
// 监听键盘事件
useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
() => {
setKeyboardVisible(true);
}
);
const keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
() => {
setKeyboardVisible(false);
}
);
return () => {
keyboardDidHideListener.remove();
keyboardDidShowListener.remove();
};
}, []);
// 检查登录状态
useEffect(() => {
// 如果是从 CountrySelect 页面来的,不显示登录提示
if (route.name === 'CountrySelect') {
return;
}
if (!isLoggedIn) {
setShowLoginPrompt(true);
Animated.parallel([
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}),
Animated.spring(slideAnim, {
toValue: 0,
tension: 50,
friction: 9,
useNativeDriver: true,
})
]).start();
} else {
// 如果已登录,关闭弹窗
handleCloseModal();
}
}, [isLoggedIn, route.name]);
const handleCloseModal = () => {
Animated.parallel([
Animated.timing(fadeAnim, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(slideAnim, {
toValue: 300,
duration: 200,
useNativeDriver: true,
})
]).start(() => {
setShowLoginPrompt(false);
});
};
const handleGoToLogin = () => {
handleCloseModal();
navigation.navigate('Login');
};
return (
<>
<Tab.Navigator
screenOptions={{
tabBarActiveTintColor: '#0066FF',
tabBarInactiveTintColor: '#999999',
tabBarStyle: {
3 weeks ago
height: Platform.OS === 'ios' ? 65 : 50,
paddingBottom: Platform.OS === 'ios' ? 15 : 5,
paddingTop: 5,
backgroundColor: '#FFFFFF',
borderTopWidth: 1,
borderTopColor: '#F0F0F0',
display: keyboardVisible ? 'none' : 'flex',
},
headerShown: false,
}}
>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
3 weeks ago
tabBarLabel: t('home'),
tabBarIcon: ({ color, size }: TabBarIconProps) => (
<IconComponent name="home-outline" size={size} color={color} />
),
}}
/>
<Tab.Screen
name="Category"
component={CategoryScreen}
options={{
3 weeks ago
tabBarLabel: t('categories'),
tabBarIcon: ({ color, size }: TabBarIconProps) => (
<IconComponent name="grid-outline" size={size} color={color} />
),
}}
/>
<Tab.Screen
name="Chat"
component={ChatScreen}
options={{
3 weeks ago
tabBarLabel: t('chat'),
tabBarIcon: ({ color, size }: TabBarIconProps) => (
<IconComponent name="chatbubble-outline" size={size} color={color} />
),
}}
/>
<Tab.Screen
name="Cart"
component={CartScreen}
options={{
3 weeks ago
tabBarLabel: t('cart'),
tabBarIcon: ({ color, size }: TabBarIconProps) => (
<IconComponent name="cart-outline" size={size} color={color} />
),
}}
/>
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{
3 weeks ago
tabBarLabel: t('my'),
tabBarIcon: ({ color, size }: TabBarIconProps) => (
<IconComponent name="person-outline" size={size} color={color} />
),
}}
/>
</Tab.Navigator>
<Modal
visible={showLoginPrompt}
transparent={true}
animationType="none"
statusBarTranslucent={true}
onRequestClose={handleCloseModal}
>
<Animated.View
style={[
styles.modalContainer,
{
opacity: fadeAnim,
}
]}
>
<Animated.View
style={[
styles.modalOverlay,
{
opacity: fadeAnim,
}
]}
>
<TouchableOpacity
style={styles.modalOverlayTouch}
activeOpacity={1}
onPress={handleCloseModal}
/>
</Animated.View>
<Animated.View
style={[
styles.modalContent,
{
transform: [{ translateY: slideAnim }],
},
]}
>
<TouchableOpacity
style={styles.closeButton}
onPress={handleCloseModal}
>
<Text style={styles.closeButtonText}></Text>
</TouchableOpacity>
<View style={styles.modalHeader}>
<View style={styles.modalIndicator} />
</View>
<Text style={styles.modalTitle}>{t('welcomeTitle')}</Text>
<Text style={styles.modalText}>
{t('welcomeMessage')}
</Text>
<TouchableOpacity
style={styles.modalButton}
onPress={handleGoToLogin}
>
<Text style={styles.modalButtonText}>{t('loginNow')}</Text>
</TouchableOpacity>
</Animated.View>
</Animated.View>
</Modal>
</>
);
};
const styles = StyleSheet.create({
modalContainer: {
flex: 1,
justifyContent: 'flex-end',
},
modalOverlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
modalOverlayTouch: {
flex: 1,
},
modalContent: {
backgroundColor: '#fff',
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
paddingHorizontal: 20,
paddingBottom: Platform.OS === 'ios' ? 40 : 20,
position: 'relative',
},
closeButton: {
position: 'absolute',
top: 20,
right: 20,
width: 24,
height: 24,
justifyContent: 'center',
alignItems: 'center',
zIndex: 1,
},
closeButtonText: {
fontSize: 20,
color: '#999',
},
modalHeader: {
alignItems: 'center',
paddingTop: 12,
paddingBottom: 20,
},
modalIndicator: {
width: 40,
height: 4,
backgroundColor: '#E0E0E0',
borderRadius: 2,
},
modalTitle: {
fontSize: 20,
fontWeight: '600',
color: '#000',
textAlign: 'center',
marginBottom: 12,
},
modalText: {
fontSize: 16,
color: '#666',
textAlign: 'center',
marginBottom: 24,
lineHeight: 22,
},
modalButton: {
backgroundColor: '#0066FF',
height: 50,
borderRadius: 25,
justifyContent: 'center',
alignItems: 'center',
},
modalButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
});