Overview
The user profile system provides comprehensive account management, gamification features, and detailed usage statistics. Users can upload profile photos, track their level progression, unlock achievements, and view their parking history.
Basic Details
Users can view and edit their core profile information:
Display Name Extracted from Firebase authentication or email
Member Since Account creation date from Firebase metadata
Profile Photo Uploaded to Firebase Storage with CDN delivery
User Level Calculated from XP and activity
Photo Upload
Users can update their profile photo with automatic cloud storage:
const pickImage = async () => {
let result = await ImagePicker . launchImageLibraryAsync ({
mediaTypes: ImagePicker . MediaTypeOptions . Images ,
allowsEditing: true ,
aspect: [ 1 , 1 ],
quality: 0.5 ,
});
if ( ! result . canceled ) {
const localUri = result . assets [ 0 ]. uri ;
setUploading ( true );
try {
await uploadImageAsync ( localUri );
Alert . alert ( "¡Éxito!" , "Tu foto de perfil se ha actualizado." );
} catch ( e ) {
Alert . alert ( "Error" , "No se pudo subir la imagen." );
} finally {
setUploading ( false );
}
}
};
Firebase Storage Integration
const uploadImageAsync = async ( uri : string ) => {
const blob = await fetch ( uri ). then ( r => r . blob ());
const user = auth . currentUser ;
if ( ! user ) return ;
const storage = getStorage ();
const storageRef = ref ( storage , `profile_images/ ${ user . uid } ` );
await uploadBytes ( storageRef , blob );
const downloadUrl = await getDownloadURL ( storageRef );
// Update Firebase Auth profile
await updateProfile ( user , { photoURL: downloadUrl });
return downloadUrl ;
};
Profile photos are automatically compressed to 50% quality to optimize storage and loading times.
Level System
Users progress through 5 levels based on accumulated XP:
Level Tiers
Level Title Min XP Max XP Benefits 1 Visitante 0 100 Getting started 2 Residente 101 220 Regular user perks 3 Dueño de la Plaza 221 350 Premium features 4 Experto en Parking 351 500 Expert benefits 5 Leyenda del Estacionamiento 501+ ∞ Maximum level
XP Calculation
const calculateXP = ( stats : UserStats ) => {
const now = new Date ();
const diffTime = Math . abs ( now . getTime () - stats . memberSinceDate . getTime ());
const monthsActive = Math . floor ( diffTime / ( 1000 * 60 * 60 * 24 * 30 ));
let xp = 0 ;
xp += stats . reservationsClean * 100 ; // 100 XP per clean reservation
xp += stats . dailyLogins * 30 ; // 30 XP per day logged in
xp += monthsActive * 150 ; // 150 XP per month active
xp += stats . friendsCount * 50 ; // 50 XP per friend
return xp ;
};
Progress Bar
Visual progress indicator shows advancement to next level:
const levelInfo = getLevelData ( currentXP );
const xpRange = levelInfo . maxXP - levelInfo . minXP ;
const xpProgress = currentXP - levelInfo . minXP ;
const progressPercent = Math . min ( Math . max ( xpProgress / xpRange , 0 ), 1 );
< View style = {styles. progressBarBackground } >
< View
style = { [
styles.progressBarFill,
{ width: ` ${ progressPercent * 100 } %` }
]}
/>
</View>
Achievements System
Users can unlock achievements through various activities:
Requirements: Complete 1 reservation
Reward: 100 XP
Color: Red (#FF5733)
Requirements: Add 4 payment methods
Reward: 500 XP
Color: Gold (#FFC300)
Requirements: Complete 20+ reservations
Reward: 300 XP
Color: Pink (#E91E63)
Requirements: 5 months active
Reward: 1000 XP
Color: Purple (#9B59B6)
Requirements: Zero penalties on all reservations
Reward: 250 XP
Color: Green (#2ECC71)
Requirements: Add 1 friend
Reward: 150 XP
Color: Blue (#2196F3)
Requirements: Add 10 friends
Reward: 1000 XP
Color: Deep Purple (#9C27B0)
Achievement Unlocking
const triggerUnlock = async (
id : string ,
title : string ,
desc : string ,
icon : string ,
xp : number
) => {
if ( existingAchievements . includes ( id ) || shownSessionAchievements . has ( id )) {
return ;
}
setTimeout ( async () => {
unlockAchievement ({ title , description: desc , icon , xp });
shownSessionAchievements . add ( id );
await updateDoc ( userRef , {
unlockedAchievements: arrayUnion ( id )
});
}, 1500 );
};
Achievements are stored in Firestore and synchronized across devices to prevent duplicate unlocks.
User Statistics
The profile displays comprehensive activity metrics:
Statistics Tracked
Total Reservations const qRes = query (
collection ( db , "reservations" ),
where ( "clientId" , "==" , user . uid )
);
const resSnap = await getDocs ( qRes );
setTotalReservations ( resSnap . size );
Clean Reservations let cleanRes = 0 ;
resSnap . forEach (( doc ) => {
const d = doc . data ();
if ( d . status === 'inactive' && ( d . penaltyApplied || 0 ) === 0 ) {
cleanRes ++ ;
}
});
Payment Cards const qCards = query (
collection ( db , "cards" ),
where ( "userId" , "==" , user . uid )
);
const cardsSnap = await getDocs ( qCards );
setCardsCount ( cardsSnap . size );
Friends Count const qFriends1 = query (
collection ( db , "friend_requests" ),
where ( "fromId" , "==" , user . uid ),
where ( "status" , "==" , "accepted" )
);
const qFriends2 = query (
collection ( db , "friend_requests" ),
where ( "toId" , "==" , user . uid ),
where ( "status" , "==" , "accepted" )
);
const [ snapF1 , snapF2 ] = await Promise . all ([
getDocs ( qFriends1 ),
getDocs ( qFriends2 )
]);
setFriendsCount ( snapF1 . size + snapF2 . size );
QR Code Integration
Users can display their personal QR code for friend connections:
const qrValue = `parkinmx: ${ userId } ` ;
< MyQRModal
visible = { showMyQR }
onClose = {() => setShowMyQR ( false )}
userId = {auth.currentUser?.uid || '' }
userName = { userName }
/>
The QR code contains the user’s Firebase UID prefixed with “parkinmx:” for app-specific identification.
Ranking Access
Users can navigate to the global leaderboard:
< TouchableOpacity onPress = {() => router.push( '/ranking' )}>
<Ionicons name= "trophy-outline" size={24} color= "black" />
</TouchableOpacity>
Real-Time XP Sync
XP is automatically synchronized to Firebase for ranking:
let finalXP = 0 ;
finalXP += cleanRes * 100 ;
finalXP += dailyLogins * 30 ;
finalXP += monthsActive * 150 ;
finalXP += totalFriends * 50 ;
await updateDoc ( userRef , { xp: finalXP });
This ensures the global ranking always reflects the most current user achievements.
Smart Avatar Component
Profile photos use cached rendering for optimal performance:
< SmartAvatar
uri = { image }
userId = {auth.currentUser?. uid }
size = { 100 }
/>
The SmartAvatar component implements:
Automatic caching
Loading states
Fallback avatars
CDN optimization