expo-popcore-old/components/profile/profile-identity.tsx

178 lines
4.1 KiB
TypeScript

import React from 'react';
import { View, TouchableOpacity, Image, type ImageSourcePropType } from 'react-native';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { ThemedText } from '@/components/themed-text';
import { deriveInitials, type IdentityStat } from '@/utils/profile-data';
export function ProfileIdentity({
displayName,
avatarSource,
avatarSize,
stats,
onEdit,
}: {
displayName: string;
avatarSource?: ImageSourcePropType;
avatarSize: number;
stats: IdentityStat[];
onEdit: () => void;
}) {
const palette = darkPalette;
return (
<View style={styles.identityRow}>
<Avatar
palette={palette}
size={avatarSize}
source={avatarSource}
fallback={displayName}
/>
<View style={styles.identityDetails}>
<View style={styles.nameRow}>
<ThemedText numberOfLines={1} style={[styles.username, { color: palette.textPrimary }]}>
{displayName}
</ThemedText>
<TouchableOpacity
onPress={onEdit}
activeOpacity={0.85}
style={[
styles.editButton,
{
borderColor: palette.border,
backgroundColor: palette.surface,
},
]}
>
<MaterialIcons name="edit" size={16} color={palette.textPrimary} style={styles.editIcon} />
<ThemedText style={[styles.editLabel, { color: palette.textPrimary }]}>Edit</ThemedText>
</TouchableOpacity>
</View>
</View>
</View>
);
}
function Avatar({
palette,
size,
source,
fallback,
}: {
palette: any;
size: number;
source?: ImageSourcePropType;
fallback: string;
}) {
const initials = deriveInitials(fallback);
return (
<View
style={[
styles.avatarShell,
{
width: size,
height: size,
borderRadius: size / 2,
backgroundColor: palette.avatarBackdrop,
},
]}
>
{source ? (
<Image source={source} style={{ width: size, height: size, borderRadius: size / 2 }} resizeMode="cover" />
) : (
<ThemedText style={[styles.avatarInitials, { color: palette.textPrimary }]}>{initials}</ThemedText>
)}
</View>
);
}
function StatItem({ palette, label, value }: { palette: any; label: string; value: number }) {
return (
<View style={styles.statItem}>
<ThemedText style={[styles.statValue, { color: palette.textPrimary }]}>{value}</ThemedText>
<ThemedText style={[styles.statLabel, { color: palette.textSecondary }]}>{label}</ThemedText>
</View>
);
}
const darkPalette = {
textPrimary: '#F6F7FA',
textSecondary: '#8E9098',
avatarBackdrop: '#1F2026',
surface: '#121216',
border: '#1D1E24',
};
const lightPalette = {
textPrimary: '#0F1320',
textSecondary: '#5E6474',
avatarBackdrop: '#D5D8E2',
surface: '#FFFFFF',
border: '#E2E5ED',
};
const styles = {
identityRow: {
flexDirection: 'row' as const,
alignItems: 'center' as const,
marginBottom: 24,
},
avatarShell: {
alignItems: 'center' as const,
justifyContent: 'center' as const,
},
avatarInitials: {
fontSize: 28,
fontWeight: '700' as const,
},
identityDetails: {
flex: 1,
marginLeft: 18,
},
nameRow: {
flexDirection: 'row' as const,
alignItems: 'center' as const,
marginBottom: 10,
},
username: {
flex: 1,
fontSize: 20,
fontWeight: '700' as const,
marginRight: 12,
},
editButton: {
flexDirection: 'row' as const,
alignItems: 'center' as const,
borderRadius: 999,
borderWidth: 1,
paddingHorizontal: 20,
paddingVertical: 8,
},
editIcon: {
marginRight: 6,
},
editLabel: {
fontSize: 14,
fontWeight: '600' as const,
},
statRow: {
flexDirection: 'row' as const,
alignItems: 'center' as const,
},
statItem: {
flexDirection: 'row' as const,
alignItems: 'baseline' as const,
marginRight: 18,
},
statValue: {
fontSize: 15,
fontWeight: '700' as const,
marginRight: 4,
},
statLabel: {
fontSize: 13,
fontWeight: '500' as const,
textTransform: 'lowercase' as const,
},
};