add profile details and features

This commit is contained in:
Joseph D'Souza 2026-02-10 16:45:29 +01:00
parent 3b571ede07
commit 2b5446555b

View File

@ -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>
);
}