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.

310 lines
7.6 KiB

import React, { useEffect, useState, useCallback } from 'react';
import {
View,
Text,
StyleSheet,
SafeAreaView,
StatusBar,
TouchableOpacity,
Modal,
Animated,
Dimensions,
BackHandler,
Platform,
} from 'react-native';
import Constants from 'expo-constants';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { RootStackParamList } from '../../App';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
type MainAppScreenNavigationProp = NativeStackNavigationProp<
RootStackParamList,
'MainApp'
>;
const MODAL_HEIGHT = 280; // 估计的模态框高度
export const MainApp = () => {
const navigation = useNavigation<MainAppScreenNavigationProp>();
const [modalVisible, setModalVisible] = useState(true);
const [slideAnim] = useState(new Animated.Value(MODAL_HEIGHT));
const [fadeAnim] = useState(new Animated.Value(0));
useEffect(() => {
if (modalVisible) {
slideAnim.setValue(MODAL_HEIGHT);
fadeAnim.setValue(0);
Animated.parallel([
Animated.spring(slideAnim, {
toValue: 0,
useNativeDriver: true,
tension: 65,
friction: 8,
velocity: 0.3,
}),
Animated.timing(fadeAnim, {
toValue: 1,
duration: 200,
useNativeDriver: true,
})
]).start();
}
}, [modalVisible]);
const handleCloseModal = useCallback(() => {
Animated.parallel([
Animated.timing(slideAnim, {
toValue: MODAL_HEIGHT,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(fadeAnim, {
toValue: 0,
duration: 200,
useNativeDriver: true,
})
]).start(() => {
setModalVisible(false);
});
}, [slideAnim, fadeAnim]);
const handleLoginPress = () => {
handleCloseModal();
// 延迟导航,等待弹窗关闭动画完成
setTimeout(() => {
navigation.navigate('Login');
}, 200);
};
useEffect(() => {
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
() => {
if (modalVisible) {
handleCloseModal();
return true;
}
return false;
}
);
return () => backHandler.remove();
}, [modalVisible, handleCloseModal]);
const handleLogout = async () => {
try {
await AsyncStorage.removeItem('@selected_country');
navigation.replace('CountrySelect');
} catch (error) {
console.error('Error logging out:', error);
}
};
return (
<SafeAreaView style={styles.safeArea}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
<View style={styles.safeAreaContent}>
<NavigationContainer>
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>{t('mainAppTitle')}</Text>
</View>
<View style={styles.content}>
<Text style={styles.text}>{t('mainAppText')}</Text>
<TouchableOpacity
style={styles.button}
onPress={handleLogout}
>
<Text style={styles.buttonText}>{t('resetCountry')}</Text>
</TouchableOpacity>
</View>
</View>
<Modal
visible={modalVisible}
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={handleLoginPress}
>
<Text style={styles.modalButtonText}>{t('loginNow')}</Text>
</TouchableOpacity>
</Animated.View>
</Animated.View>
</Modal>
</NavigationContainer>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: Constants.statusBarHeight,
},
safeArea: {
flex: 1,
backgroundColor: '#fff',
},
safeAreaContent: {
flex: 1,
paddingTop: Platform.OS === 'android' ? 10 : 0,
},
header: {
padding: 20,
backgroundColor: '#fff',
borderBottomWidth: 1,
borderBottomColor: '#f0f0f0',
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#000',
},
content: {
flex: 1,
padding: 20,
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 16,
color: '#333',
marginBottom: 30,
},
button: {
backgroundColor: '#007AFF',
paddingHorizontal: 20,
paddingVertical: 12,
borderRadius: 8,
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '500',
},
modalContainer: {
flex: 1,
justifyContent: 'flex-end',
},
modalOverlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.4)',
},
modalOverlayTouch: {
flex: 1,
},
modalContent: {
backgroundColor: '#fff',
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
padding: 20,
paddingBottom: 40,
},
modalHeader: {
alignItems: 'center',
marginBottom: 20,
},
modalIndicator: {
width: 40,
height: 4,
backgroundColor: '#DDD',
borderRadius: 2,
marginVertical: 8,
},
modalTitle: {
fontSize: 22,
fontWeight: 'bold',
marginBottom: 16,
color: '#000',
},
modalText: {
fontSize: 16,
color: '#666',
marginBottom: 24,
lineHeight: 22,
},
modalButton: {
backgroundColor: '#007AFF',
paddingVertical: 14,
borderRadius: 10,
alignItems: 'center',
},
modalButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
closeButton: {
position: 'absolute',
top: 20,
right: 20,
width: 24,
height: 24,
alignItems: 'center',
justifyContent: 'center',
zIndex: 1,
},
closeButtonText: {
fontSize: 20,
color: '#999',
fontWeight: '400',
},
});