add profile details and features
This commit is contained in:
parent
3b571ede07
commit
2b5446555b
@ -1,18 +1,266 @@
|
||||
import React from 'react';
|
||||
import { View, Text, useColorScheme } from 'react-native';
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text, TouchableOpacity, useColorScheme, ScrollView, Alert, Image, ActivityIndicator, Modal } from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { RootStackParamList } from '../navigation/AppNavigator';
|
||||
import { getThemeStyles, commonStyles } from '../theme/styles';
|
||||
import { useAuthStore } from '../store/useAuthStore';
|
||||
import { authService } from '../services/api';
|
||||
import { COLORS } from '../theme/colors';
|
||||
import appConfig from '../../app.json';
|
||||
|
||||
export default function ProfileScreen() {
|
||||
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
|
||||
const { user, logout } = useAuthStore();
|
||||
const [isResending, setIsResending] = useState(false);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const colorScheme = useColorScheme();
|
||||
const isDark = colorScheme === 'dark';
|
||||
const themeStyles = getThemeStyles(isDark);
|
||||
const themeColors = isDark ? COLORS.DARK : COLORS.LIGHT;
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
const displayName =
|
||||
(user.user?.first_name && user.user?.last_name) ? `${user.user.first_name} ${user.user.last_name}` :
|
||||
user.user?.username ? user.user.username :
|
||||
user.user?.email ? user.user.email.split('@')[0] :
|
||||
'User Name';
|
||||
|
||||
const handleResendVerification = async () => {
|
||||
setIsResending(true);
|
||||
const result = await authService.resendVerificationEmail();
|
||||
setIsResending(false);
|
||||
|
||||
if (result.ok) {
|
||||
Alert.alert('Email Sent', 'Please check your email (or server logs) for the verification link.');
|
||||
} else {
|
||||
Alert.alert('Error', result.message || 'Failed to resend verification email');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteAccount = () => {
|
||||
Alert.alert(
|
||||
'Delete Account',
|
||||
'Are you sure you want to permanently delete your account? This action cannot be undone.',
|
||||
[
|
||||
{ text: 'Cancel', style: 'cancel' },
|
||||
{
|
||||
text: 'Delete',
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
const result = await authService.deleteAccount();
|
||||
if (result.ok) {
|
||||
logout();
|
||||
Alert.alert('Success', 'Your account has been deleted.');
|
||||
} else {
|
||||
Alert.alert('Error', 'Failed to delete account.');
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
const MenuItem = ({ icon, label, onPress, isDestructive = false }: { icon: string, label: string, onPress: () => void, isDestructive?: boolean }) => (
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 16,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: isDark ? '#2d3748' : '#edf2f7',
|
||||
}}
|
||||
onPress={onPress}
|
||||
>
|
||||
<View style={{
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 18,
|
||||
backgroundColor: isDestructive ? '#fff5f5' : (isDark ? '#2d3748' : '#ebf8ff'),
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginRight: 16
|
||||
}}>
|
||||
<Ionicons
|
||||
name={icon as any}
|
||||
size={20}
|
||||
color={isDestructive ? '#e53e3e' : themeColors.brand}
|
||||
/>
|
||||
</View>
|
||||
<Text style={{
|
||||
flex: 1,
|
||||
fontSize: 16,
|
||||
color: isDestructive ? '#e53e3e' : themeColors.text,
|
||||
fontWeight: '500'
|
||||
}}>
|
||||
{label}
|
||||
</Text>
|
||||
<Ionicons name="chevron-forward" size={20} color={isDark ? '#718096' : '#cbd5e0'} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={themeStyles.container}>
|
||||
<View style={commonStyles.centered}>
|
||||
<Text style={themeStyles.headerTitle}>Profile Screen</Text>
|
||||
<Text style={themeStyles.subtitle}>User profile details will go here.</Text>
|
||||
<ScrollView
|
||||
style={themeStyles.container}
|
||||
contentContainerStyle={{ paddingBottom: 40 }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
{!user.user?.email_verified_at && (
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
backgroundColor: '#fffaf0',
|
||||
padding: 12,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#feebc8'
|
||||
}}
|
||||
onPress={handleResendVerification}
|
||||
disabled={isResending}
|
||||
>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', flex: 1 }}>
|
||||
<Ionicons name="warning-outline" size={20} color="#ed8936" />
|
||||
<Text style={{ marginLeft: 10, color: '#c05621', fontSize: 14, flex: 1 }}>
|
||||
Your email is not verified. Tap to resend link.
|
||||
</Text>
|
||||
</View>
|
||||
{isResending ? (
|
||||
<ActivityIndicator size="small" color="#c05621" />
|
||||
) : (
|
||||
<Ionicons name="arrow-forward" size={18} color="#c05621" />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
{/* Header Profile Section */}
|
||||
<View style={{ alignItems: 'center', paddingTop: 40, paddingBottom: 30 }}>
|
||||
<View style={{
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: 50,
|
||||
backgroundColor: 'white',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: 16,
|
||||
shadowColor: themeColors.brand,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 8,
|
||||
elevation: 6,
|
||||
overflow: 'hidden', // Ensure image clips to circle
|
||||
}}>
|
||||
{user.user?.avatar ? (
|
||||
<TouchableOpacity onPress={() => setIsModalVisible(true)} style={{ width: '100%', height: '100%' }}>
|
||||
<Image
|
||||
source={{ uri: user.user.avatar }}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<Text style={{ fontSize: 40, fontWeight: 'bold', color: 'white' }}>
|
||||
{displayName.charAt(0).toUpperCase()}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
<Text style={{ fontSize: 24, fontWeight: 'bold', color: themeColors.text, marginBottom: 4 }}>
|
||||
{displayName}
|
||||
</Text>
|
||||
<Text style={{ fontSize: 16, color: themeColors.subtitle }}>
|
||||
{user.user?.email}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Settings Section */}
|
||||
<View style={{
|
||||
backgroundColor: themeColors.card,
|
||||
marginHorizontal: 20,
|
||||
borderRadius: 16,
|
||||
paddingHorizontal: 20,
|
||||
marginBottom: 20,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 4,
|
||||
elevation: 2
|
||||
}}>
|
||||
<MenuItem icon="person-outline" label="Edit Profile" onPress={() => navigation.navigate('EditProfile')} />
|
||||
<MenuItem icon="notifications-outline" label="Notifications" onPress={() => {}} />
|
||||
<MenuItem icon="key-outline" label="API Token" onPress={() => Alert.alert('Your Token', user.token)} />
|
||||
<MenuItem icon="help-circle-outline" label="Help & Support" onPress={() => {}} />
|
||||
</View>
|
||||
|
||||
{/* Danger Zone */}
|
||||
<View style={{
|
||||
backgroundColor: themeColors.card,
|
||||
marginHorizontal: 20,
|
||||
borderRadius: 16,
|
||||
paddingHorizontal: 20,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 4,
|
||||
elevation: 2
|
||||
}}>
|
||||
<MenuItem icon="log-out-outline" label="Sign Out" onPress={logout} />
|
||||
<MenuItem icon="trash-outline" label="Delete Account" onPress={handleDeleteAccount} isDestructive />
|
||||
</View>
|
||||
|
||||
<Text style={{
|
||||
textAlign: 'center',
|
||||
marginTop: 30,
|
||||
color: themeColors.subtitle,
|
||||
fontSize: 12
|
||||
}}>
|
||||
Version {appConfig.expo.version}
|
||||
</Text>
|
||||
|
||||
<Modal
|
||||
visible={isModalVisible}
|
||||
transparent={true}
|
||||
animationType="fade"
|
||||
onRequestClose={() => setIsModalVisible(false)}
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0,0,0,0.90)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
activeOpacity={1}
|
||||
onPress={() => setIsModalVisible(false)}
|
||||
>
|
||||
{user.user?.avatar && (
|
||||
<View style={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
borderRadius: 150, // Circular container
|
||||
backgroundColor: 'black',
|
||||
overflow: 'hidden',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderWidth: 0.5,
|
||||
borderColor: 'white'
|
||||
}}>
|
||||
<Image
|
||||
source={{ uri: user.user.avatar }}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
style={{ position: 'absolute', top: 50, right: 20, padding: 10 }}
|
||||
onPress={() => setIsModalVisible(false)}
|
||||
>
|
||||
<Ionicons name="close" size={30} color="white" />
|
||||
</TouchableOpacity>
|
||||
</TouchableOpacity>
|
||||
</Modal>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user