Skip to main content

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.

Profile Information

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

LevelTitleMin XPMax XPBenefits
1Visitante0100Getting started
2Residente101220Regular user perks
3Dueño de la Plaza221350Premium features
4Experto en Parking351500Expert benefits
5Leyenda del Estacionamiento501+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

Build docs developers (and LLMs) love