diff --git a/app/src/main/java/eu/brassepc/fitnessdroid/data/GymRepository.kt b/app/src/main/java/eu/brassepc/fitnessdroid/data/GymRepository.kt index af49e28..ff4f0d4 100644 --- a/app/src/main/java/eu/brassepc/fitnessdroid/data/GymRepository.kt +++ b/app/src/main/java/eu/brassepc/fitnessdroid/data/GymRepository.kt @@ -144,39 +144,59 @@ class GymRepository( private suspend fun refreshReferenceData() { val token = auth.bearerToken() - val groups = client.execute("query{muscleGroups{id name iconKey}}", token = token) - db.cacheDao().upsertMuscleGroups( - groups["muscleGroups"]!!.jsonArray.map { - val o = it.jsonObject - CachedMuscleGroup( - o["id"]!!.jsonPrimitive.int, - o["name"]!!.jsonPrimitive.content, - o["iconKey"]?.jsonPrimitive?.contentOrNull(), - ) + // Varje sektion för sig, och de nya API-fälten (iconKey, + // defaultRestSeconds) med fallback — en äldre server ska aldrig + // kunna lämna hela cachen tom. + runCatching { + val groups = try { + client.execute("query{muscleGroups{id name iconKey}}", token = token) + } catch (e: GraphQlException) { + client.execute("query{muscleGroups{id name}}", token = token) } - ) + db.cacheDao().upsertMuscleGroups( + groups["muscleGroups"]!!.jsonArray.map { + val o = it.jsonObject + CachedMuscleGroup( + o["id"]!!.jsonPrimitive.int, + o["name"]!!.jsonPrimitive.content, + o["iconKey"]?.jsonPrimitive?.contentOrNull(), + ) + } + ) + } - val muscles = client.execute("query{muscles{id name muscleGroupId}}", token = token) - db.cacheDao().upsertMuscles( - muscles["muscles"]!!.jsonArray.map { - val o = it.jsonObject - CachedMuscle( - o["id"]!!.jsonPrimitive.int, - o["name"]!!.jsonPrimitive.content, - o["muscleGroupId"]?.jsonPrimitive?.intOrNull, - ) - } - ) + runCatching { + val muscles = client.execute("query{muscles{id name muscleGroupId}}", token = token) + db.cacheDao().upsertMuscles( + muscles["muscles"]!!.jsonArray.map { + val o = it.jsonObject + CachedMuscle( + o["id"]!!.jsonPrimitive.int, + o["name"]!!.jsonPrimitive.content, + o["muscleGroupId"]?.jsonPrimitive?.intOrNull, + ) + } + ) + } val favIds: Set = runCatching { client.execute("query{favoriteExercises{id}}", token = token)["favoriteExercises"]!! .jsonArray.map { it.jsonObject["id"]!!.jsonPrimitive.int }.toSet() }.getOrDefault(emptySet()) - val types = client.execute( - "query{exerciseTypes{id name description metValue tracksWeight tracksReps tracksDistance tracksDuration isBodyweight defaultRestSeconds exerciseTypeMuscles{muscle{id}}}}", - token = token, - ) + val typeFields = + "id name description metValue tracksWeight tracksReps tracksDistance tracksDuration isBodyweight" + val types = try { + client.execute( + "query{exerciseTypes{$typeFields defaultRestSeconds exerciseTypeMuscles{muscle{id}}}}", + token = token, + ) + } catch (e: GraphQlException) { + client.execute( + "query{exerciseTypes{$typeFields exerciseTypeMuscles{muscle{id}}}}", + token = token, + ) + } db.cacheDao().replaceExerciseTypes( types["exerciseTypes"]!!.jsonArray.map { val o = it.jsonObject diff --git a/app/src/main/java/eu/brassepc/fitnessdroid/ui/picker/ExercisePickerScreen.kt b/app/src/main/java/eu/brassepc/fitnessdroid/ui/picker/ExercisePickerScreen.kt index 2a3c24a..954a100 100644 --- a/app/src/main/java/eu/brassepc/fitnessdroid/ui/picker/ExercisePickerScreen.kt +++ b/app/src/main/java/eu/brassepc/fitnessdroid/ui/picker/ExercisePickerScreen.kt @@ -180,12 +180,26 @@ fun ExercisePickerScreen( } if (uiState.exercises.isEmpty()) { item { - Text( - "Inga övningar matchar — testa ett annat filter.", - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.padding(16.dp), - ) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + if (uiState.query.isBlank() && uiState.selectedGroupId == null) { + "Övningslistan är tom — hämtar från servern …" + } else { + "Inga övningar matchar — testa ett annat filter." + }, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + androidx.compose.material3.FilledTonalButton( + onClick = viewModel::refresh, + modifier = Modifier.padding(top = 12.dp), + ) { Text("Uppdatera från servern") } + } } } } diff --git a/app/src/main/java/eu/brassepc/fitnessdroid/ui/picker/PickerViewModel.kt b/app/src/main/java/eu/brassepc/fitnessdroid/ui/picker/PickerViewModel.kt index fb278f5..b382e73 100644 --- a/app/src/main/java/eu/brassepc/fitnessdroid/ui/picker/PickerViewModel.kt +++ b/app/src/main/java/eu/brassepc/fitnessdroid/ui/picker/PickerViewModel.kt @@ -43,6 +43,16 @@ class PickerViewModel(private val repo: GymRepository) : ViewModel() { private val selectedGroup = MutableStateFlow(null) private val selectedMuscle = MutableStateFlow(null) + init { + // Tom cache (t.ex. första start eller misslyckad förra hämtning)? + // Försök fylla den direkt när väljaren öppnas. + viewModelScope.launch { + if (repo.exerciseTypes.first().isEmpty()) repo.refreshAllAsync() + } + } + + fun refresh() = repo.refreshAllAsync() + private data class Filters(val query: String, val groupId: Int?, val muscleId: Int?) private val filters = combine(query, selectedGroup, selectedMuscle) { q, g, m -> Filters(q, g, m) }