Statistik: favoritövningar överst i PB-listan (stjärna på korten, återanvänder toggleFavoriteExercise)
All checks were successful
release / build-release (push) Successful in 4m31s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AFvSx1pF9pymyEuK7MFkEG
This commit is contained in:
2026-09-06 22:13:09 +02:00
parent c2161869bb
commit 8f7cd9a419
3 changed files with 35 additions and 6 deletions

View File

@@ -302,7 +302,7 @@ class GymRepository(
/** PB-lista med detaljer per rekord. mode: all | competition | training. */
suspend fun personalBestsDetailed(mode: String = "all"): List<PbEntry> {
val pb = client.execute(
"query(\$m:String){personalBests(mode:\$m){exerciseTypeId exerciseTypeName repRecords{reps weight date liftId notes status statusNote sessionName estimated1Rm}}}",
"query(\$m:String){personalBests(mode:\$m){exerciseTypeId exerciseTypeName isFavorite repRecords{reps weight date liftId notes status statusNote sessionName estimated1Rm}}}",
buildJsonObject { put("m", mode) },
auth.bearerToken(),
)
@@ -311,6 +311,7 @@ class GymRepository(
PbEntry(
exerciseName = po["exerciseTypeName"]?.jsonPrimitive?.contentOrNull() ?: "Övning",
exerciseTypeId = po["exerciseTypeId"]?.jsonPrimitive?.intOrNull ?: 0,
isFavorite = po["isFavorite"]?.jsonPrimitive?.booleanOrNull ?: false,
records = po["repRecords"]!!.jsonArray.mapNotNull { r ->
val ro = r.jsonObject
PbRecord(
@@ -326,7 +327,7 @@ class GymRepository(
)
}.sortedBy { it.reps },
)
}.sortedBy { it.exerciseName }
}.sortedWith(compareByDescending<PbEntry> { it.isFavorite }.thenBy { it.exerciseName })
}
/** Topplista för övning × reps, alla statusar (så bortplockade kan återställas). */
@@ -944,7 +945,13 @@ data class PbRecord(
val sessionName: String? = null,
val est1Rm: Double = 0.0,
)
data class PbEntry(val exerciseName: String, val records: List<PbRecord>, val exerciseTypeId: Int = 0)
data class PbEntry(
val exerciseName: String,
val records: List<PbRecord>,
val exerciseTypeId: Int = 0,
/** Favoritövning (samma FavoriteExercise som övningsväljaren) ligger överst i PB-listan. */
val isFavorite: Boolean = false,
)
/** Ett lyft med allt PB-arket behöver (pbCandidates / liftDetail). */
data class LiftDetail(

View File

@@ -5,6 +5,8 @@ import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.outlined.StarOutline
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -145,6 +147,15 @@ class StatsViewModel(
}
}
/** Favorit av/på för en övning: optimistisk omsortering, servern uppdateras i bakgrunden. */
fun togglePbFavorite(entry: eu.brassepc.fitnessdroid.data.PbEntry) {
val next = !entry.isFavorite
pbs.value = pbs.value
.map { if (it.exerciseTypeId == entry.exerciseTypeId) it.copy(isFavorite = next) else it }
.sortedWith(compareByDescending<eu.brassepc.fitnessdroid.data.PbEntry> { it.isFavorite }.thenBy { it.exerciseName })
repo.toggleFavoriteExercise(entry.exerciseTypeId, next)
}
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
@@ -408,8 +419,18 @@ fun StatsScreen(viewModel: StatsViewModel = viewModel(factory = StatsViewModel.F
}
items(pbs, key = { "${it.exerciseTypeId}-${it.exerciseName}" }) { pb ->
Card(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(14.dp)) {
Text(pb.exerciseName, style = MaterialTheme.typography.titleSmall)
Column(modifier = Modifier.padding(start = 14.dp, end = 6.dp, top = 4.dp, bottom = 14.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(pb.exerciseName, style = MaterialTheme.typography.titleSmall, modifier = Modifier.weight(1f))
// Favorit = ligger överst (samma favoriter som övningsväljaren)
androidx.compose.material3.IconButton(onClick = { viewModel.togglePbFavorite(pb) }) {
androidx.compose.material3.Icon(
if (pb.isFavorite) Icons.Filled.Star else Icons.Outlined.StarOutline,
contentDescription = if (pb.isFavorite) "Ta bort favorit" else "Gör till favorit",
tint = if (pb.isFavorite) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
Row(
modifier = Modifier
.padding(top = 6.dp)