This commit is contained in:
mac
2026-02-24 12:46:27 -05:00
parent 93c8814b84
commit c191394ee6
2 changed files with 193 additions and 188 deletions

View File

@@ -21,200 +21,202 @@ import com.google.firebase.firestore.FirebaseFirestore
@Composable @Composable
fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) { fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) {
var age by remember { mutableStateOf(prefs.getInt("age", 25).toString()) } Surface(modifier = Modifier.fillMaxSize()) {
var heightCm by remember { mutableStateOf(prefs.getString("height_cm", "170") ?: "170") } var age by remember { mutableStateOf(prefs.getInt("age", 25).toString()) }
var weight by remember { mutableStateOf(prefs.getString("weight_display", "70") ?: "70") } var heightCm by remember { mutableStateOf(prefs.getString("height_cm", "170") ?: "170") }
var isLbs by remember { mutableStateOf(prefs.getBoolean("is_lbs", false)) } var weight by remember { mutableStateOf(prefs.getString("weight_display", "70") ?: "70") }
var gender by remember { mutableStateOf(prefs.getString("gender", "Homme") ?: "Homme") } var isLbs by remember { mutableStateOf(prefs.getBoolean("is_lbs", false)) }
var activityLevel by remember { mutableStateOf(prefs.getString("activity_level", "Sédentaire") ?: "Sédentaire") } var gender by remember { mutableStateOf(prefs.getString("gender", "Homme") ?: "Homme") }
var goal by remember { mutableStateOf(prefs.getString("goal", "Maintenir le poids") ?: "Maintenir le poids") } var activityLevel by remember { mutableStateOf(prefs.getString("activity_level", "Sédentaire") ?: "Sédentaire") }
var isDiabetic by remember { mutableStateOf(prefs.getBoolean("is_diabetic", false)) } var goal by remember { mutableStateOf(prefs.getString("goal", "Maintenir le poids") ?: "Maintenir le poids") }
var isDiabetic by remember { mutableStateOf(prefs.getBoolean("is_diabetic", false)) }
val context = LocalContext.current val context = LocalContext.current
val activityLevels = listOf("Sédentaire", "Légèrement actif", "Modérément actif", "Très actif", "Extrêmement actif") val activityLevels = listOf("Sédentaire", "Légèrement actif", "Modérément actif", "Très actif", "Extrêmement actif")
val goals = listOf("Maintenir le poids", "Perdre du poids") val goals = listOf("Maintenir le poids", "Perdre du poids")
val activityMultipliers = mapOf( val activityMultipliers = mapOf(
"Sédentaire" to 1.2, "Sédentaire" to 1.2,
"Légèrement actif" to 1.375, "Légèrement actif" to 1.375,
"Modérément actif" to 1.55, "Modérément actif" to 1.55,
"Très actif" to 1.725, "Très actif" to 1.725,
"Extrêmement actif" to 1.9 "Extrêmement actif" to 1.9
)
Column(
modifier = Modifier
.fillMaxSize()
.padding(24.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Configuration du profil", style = MaterialTheme.typography.headlineLarge)
Spacer(Modifier.height(24.dp))
Text("Votre objectif :", style = MaterialTheme.typography.titleMedium, modifier = Modifier.align(Alignment.Start))
goals.forEach { g ->
Row(
Modifier
.fillMaxWidth()
.clickable { goal = g }
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(selected = goal == g, onClick = { goal = g })
Text(g)
}
}
Spacer(Modifier.height(16.dp))
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text("Genre : ", Modifier.width(80.dp))
RadioButton(selected = gender == "Homme", onClick = { gender = "Homme" })
Text("Homme")
Spacer(Modifier.width(16.dp))
RadioButton(selected = gender == "Femme", onClick = { gender = "Femme" })
Text("Femme")
}
Spacer(Modifier.height(16.dp))
OutlinedTextField(
value = age,
onValueChange = { age = it },
label = { Text("Âge") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.fillMaxWidth()
) )
Spacer(Modifier.height(16.dp)) Column(
OutlinedTextField(
value = heightCm,
onValueChange = { heightCm = it },
label = { Text("Taille (cm)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(16.dp))
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
OutlinedTextField(
value = weight,
onValueChange = { weight = it },
label = { Text(if (isLbs) "Poids (lbs)" else "Poids (kg)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.weight(1f)
)
Spacer(Modifier.width(16.dp))
Switch(checked = isLbs, onCheckedChange = { isLbs = it })
Text(if (isLbs) "lbs" else "kg")
}
Spacer(Modifier.height(16.dp))
Text("Niveau d'activité :", style = MaterialTheme.typography.titleMedium, modifier = Modifier.align(Alignment.Start))
activityLevels.forEach { level ->
Row(
Modifier
.fillMaxWidth()
.clickable { activityLevel = level }
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(selected = activityLevel == level, onClick = { activityLevel = level })
Text(level)
}
}
Spacer(Modifier.height(16.dp))
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
Checkbox(checked = isDiabetic, onCheckedChange = { isDiabetic = it })
Text("Je suis diabétique")
}
Spacer(Modifier.height(32.dp))
Button(
onClick = {
val currentUser = FirebaseAuth.getInstance().currentUser
if (currentUser == null) {
Toast.makeText(context, "Erreur : Vous devez être connecté pour sauvegarder.", Toast.LENGTH_LONG).show()
return@Button
}
val ageInt = age.toIntOrNull() ?: 0
val height = heightCm.toDoubleOrNull() ?: 0.0
var weightKg = weight.toDoubleOrNull() ?: 0.0
val weightDisplay = weight
if (isLbs) weightKg *= 0.453592
val bmr = if (gender == "Homme") {
(10 * weightKg) + (6.25 * height) - (5 * ageInt) + 5
} else {
(10 * weightKg) + (6.25 * height) - (5 * ageInt) - 161
}
val multiplier = activityMultipliers[activityLevel] ?: 1.2
var targetCals = (bmr * multiplier).toInt()
if (goal == "Perdre du poids") targetCals -= 500
val targetCarbs = (targetCals * 0.5 / 4).toInt()
val targetProtein = (targetCals * 0.2 / 4).toInt()
val targetFat = (targetCals * 0.3 / 9).toInt()
val userProfile = hashMapOf(
"age" to ageInt,
"height_cm" to height,
"weight_kg" to weightKg,
"is_lbs" to isLbs,
"gender" to gender,
"activity_level" to activityLevel,
"goal" to goal,
"is_diabetic" to isDiabetic,
"target_calories" to targetCals,
"target_carbs" to targetCarbs,
"target_protein" to targetProtein,
"target_fat" to targetFat
)
// On spécifie explicitement la base de données "scan-wich"
FirebaseFirestore.getInstance("scan-wich").collection("users").document(currentUser.uid)
.set(userProfile)
.addOnSuccessListener {
Log.d("SetupScreen", "User profile saved to Firestore.")
prefs.edit {
putString("target_calories", targetCals.toString())
putString("target_carbs", targetCarbs.toString())
putString("target_protein", targetProtein.toString())
putString("target_fat", targetFat.toString())
putString("weight_kg", weightKg.toString())
putString("weight_display", weightDisplay)
putBoolean("is_lbs", isLbs)
putString("height_cm", heightCm)
putBoolean("is_diabetic", isDiabetic)
putInt("age", ageInt)
putString("gender", gender)
putString("activity_level", activityLevel)
putString("goal", goal)
}
Toast.makeText(context, "Profil sauvegardé sur votre compte !", Toast.LENGTH_SHORT).show()
onComplete()
}
.addOnFailureListener { e ->
Log.w("SetupScreen", "Error writing user profile to Firestore", e)
Toast.makeText(context, "Erreur de sauvegarde du profil: ${e.message}", Toast.LENGTH_LONG).show()
}
},
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxSize()
.height(56.dp), .padding(24.dp)
enabled = age.isNotBlank() && heightCm.isNotBlank() && weight.isNotBlank() .verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Text("Sauvegarder le profil") Text("Configuration du profil", style = MaterialTheme.typography.headlineLarge)
Spacer(Modifier.height(24.dp))
Text("Votre objectif :", style = MaterialTheme.typography.titleMedium, modifier = Modifier.align(Alignment.Start))
goals.forEach { g ->
Row(
Modifier
.fillMaxWidth()
.clickable { goal = g }
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(selected = goal == g, onClick = { goal = g })
Text(g)
}
}
Spacer(Modifier.height(16.dp))
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text("Genre : ", Modifier.width(80.dp))
RadioButton(selected = gender == "Homme", onClick = { gender = "Homme" })
Text("Homme")
Spacer(Modifier.width(16.dp))
RadioButton(selected = gender == "Femme", onClick = { gender = "Femme" })
Text("Femme")
}
Spacer(Modifier.height(16.dp))
OutlinedTextField(
value = age,
onValueChange = { age = it },
label = { Text("Âge") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(16.dp))
OutlinedTextField(
value = heightCm,
onValueChange = { heightCm = it },
label = { Text("Taille (cm)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(16.dp))
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
OutlinedTextField(
value = weight,
onValueChange = { weight = it },
label = { Text(if (isLbs) "Poids (lbs)" else "Poids (kg)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.weight(1f)
)
Spacer(Modifier.width(16.dp))
Switch(checked = isLbs, onCheckedChange = { isLbs = it })
Text(if (isLbs) "lbs" else "kg")
}
Spacer(Modifier.height(16.dp))
Text("Niveau d'activité :", style = MaterialTheme.typography.titleMedium, modifier = Modifier.align(Alignment.Start))
activityLevels.forEach { level ->
Row(
Modifier
.fillMaxWidth()
.clickable { activityLevel = level }
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(selected = activityLevel == level, onClick = { activityLevel = level })
Text(level)
}
}
Spacer(Modifier.height(16.dp))
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
Checkbox(checked = isDiabetic, onCheckedChange = { isDiabetic = it })
Text("Je suis diabétique")
}
Spacer(Modifier.height(32.dp))
Button(
onClick = {
val currentUser = FirebaseAuth.getInstance().currentUser
if (currentUser == null) {
Toast.makeText(context, "Erreur : Vous devez être connecté pour sauvegarder.", Toast.LENGTH_LONG).show()
return@Button
}
val ageInt = age.toIntOrNull() ?: 0
val height = heightCm.toDoubleOrNull() ?: 0.0
var weightKg = weight.toDoubleOrNull() ?: 0.0
val weightDisplay = weight
if (isLbs) weightKg *= 0.453592
val bmr = if (gender == "Homme") {
(10 * weightKg) + (6.25 * height) - (5 * ageInt) + 5
} else {
(10 * weightKg) + (6.25 * height) - (5 * ageInt) - 161
}
val multiplier = activityMultipliers[activityLevel] ?: 1.2
var targetCals = (bmr * multiplier).toInt()
if (goal == "Perdre du poids") targetCals -= 500
val targetCarbs = (targetCals * 0.5 / 4).toInt()
val targetProtein = (targetCals * 0.2 / 4).toInt()
val targetFat = (targetCals * 0.3 / 9).toInt()
val userProfile = hashMapOf(
"age" to ageInt,
"height_cm" to height,
"weight_kg" to weightKg,
"is_lbs" to isLbs,
"gender" to gender,
"activity_level" to activityLevel,
"goal" to goal,
"is_diabetic" to isDiabetic,
"target_calories" to targetCals,
"target_carbs" to targetCarbs,
"target_protein" to targetProtein,
"target_fat" to targetFat
)
// On spécifie explicitement la base de données "scan-wich"
FirebaseFirestore.getInstance("scan-wich").collection("users").document(currentUser.uid)
.set(userProfile)
.addOnSuccessListener {
Log.d("SetupScreen", "User profile saved to Firestore.")
prefs.edit {
putString("target_calories", targetCals.toString())
putString("target_carbs", targetCarbs.toString())
putString("target_protein", targetProtein.toString())
putString("target_fat", targetFat.toString())
putString("weight_kg", weightKg.toString())
putString("weight_display", weightDisplay)
putBoolean("is_lbs", isLbs)
putString("height_cm", heightCm)
putBoolean("is_diabetic", isDiabetic)
putInt("age", ageInt)
putString("gender", gender)
putString("activity_level", activityLevel)
putString("goal", goal)
}
Toast.makeText(context, "Profil sauvegardé sur votre compte !", Toast.LENGTH_SHORT).show()
onComplete()
}
.addOnFailureListener { e ->
Log.w("SetupScreen", "Error writing user profile to Firestore", e)
Toast.makeText(context, "Erreur de sauvegarde du profil: ${e.message}", Toast.LENGTH_LONG).show()
}
},
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
enabled = age.isNotBlank() && heightCm.isNotBlank() && weight.isNotBlank()
) {
Text("Sauvegarder le profil")
}
} }
} }
} }

View File

@@ -2,6 +2,9 @@
**Changements majeurs de la version actuelle :** **Changements majeurs de la version actuelle :**
🎨 **Améliorations de l'Interface Utilisateur :**
- **Correction du Thème :** Résolution d'un problème de contraste sur l'écran de configuration du profil qui rendait le texte illisible (texte jaune sur fond blanc). L'écran respecte désormais correctement le thème de l'application.
🛡️ **Refonte de l'Architecture de Sécurité et de Données :** 🛡️ **Refonte de l'Architecture de Sécurité et de Données :**
- **Gestion des accès centralisée :** L'ancien système d'utilisateurs autorisés codé en dur dans l'application a été supprimé. L'accès est désormais contrôlé de manière sécurisée et dynamique via une liste d'autorisation sur le serveur Firebase Firestore. Cela ouvre la voie à la gestion d'abonnements. - **Gestion des accès centralisée :** L'ancien système d'utilisateurs autorisés codé en dur dans l'application a été supprimé. L'accès est désormais contrôlé de manière sécurisée et dynamique via une liste d'autorisation sur le serveur Firebase Firestore. Cela ouvre la voie à la gestion d'abonnements.
- **Profils Utilisateurs dans le Cloud :** Les données de profil (poids, objectifs, etc.) sont maintenant synchronisées avec le compte Firebase de l'utilisateur, permettant une expérience cohérente sur plusieurs appareils. - **Profils Utilisateurs dans le Cloud :** Les données de profil (poids, objectifs, etc.) sont maintenant synchronisées avec le compte Firebase de l'utilisateur, permettant une expérience cohérente sur plusieurs appareils.