Fix Mannschaften

This commit is contained in:
Torsten Schulz (local)
2026-06-10 08:03:44 +02:00
parent 530e544542
commit e8a50e55ca
5 changed files with 36 additions and 20 deletions

View File

@@ -10,8 +10,12 @@ sealed class Destinations(val route: String) {
object Links : Destinations("verein/links") object Links : Destinations("verein/links")
object Impressum : Destinations("impressum") object Impressum : Destinations("impressum")
object Mannschaften : Destinations("mannschaften") object Mannschaften : Destinations("mannschaften")
object MannschaftDetail : Destinations("mannschaften/{slug}") { object MannschaftDetail : Destinations("mannschaften/{slug}?season={season}") {
fun create(slug: String): String = "mannschaften/$slug" fun create(slug: String, season: String? = null): String {
val encodedSlug = android.net.Uri.encode(slug)
val selectedSeason = season?.takeIf { it.isNotBlank() } ?: return "mannschaften/$encodedSlug"
return "mannschaften/$encodedSlug?season=${android.net.Uri.encode(selectedSeason)}"
}
} }
object MannschaftLegacyDetail : Destinations("mannschaft/{slug}") { object MannschaftLegacyDetail : Destinations("mannschaft/{slug}") {
fun create(slug: String): String = "mannschaft/$slug" fun create(slug: String): String = "mannschaft/$slug"

View File

@@ -17,6 +17,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -32,7 +33,9 @@ fun NavGraph(
val backStackEntry = navController.currentBackStackEntryAsState().value val backStackEntry = navController.currentBackStackEntryAsState().value
val route = backStackEntry?.destination?.route val route = backStackEntry?.destination?.route
val currentRoute = if (route == Destinations.MannschaftDetail.route) { val currentRoute = if (route == Destinations.MannschaftDetail.route) {
backStackEntry.arguments?.getString("slug")?.let(Destinations.MannschaftDetail::create) backStackEntry.arguments?.getString("slug")?.let { slug ->
Destinations.MannschaftDetail.create(slug, backStackEntry.arguments?.getString("season"))
}
} else route } else route
val navigationState by navigationViewModel.state.collectAsState() val navigationState by navigationViewModel.state.collectAsState()
LaunchedEffect(currentRoute) { LaunchedEffect(currentRoute) {
@@ -136,9 +139,13 @@ fun NavGraph(
composable("mannschaften/jugend") { composable("mannschaften/jugend") {
de.harheimertc.ui.screens.mannschaften.MannschaftenScreen(navController, !persistentNavigation) de.harheimertc.ui.screens.mannschaften.MannschaftenScreen(navController, !persistentNavigation)
} }
composable(Destinations.MannschaftDetail.route) { entry -> composable(
route = Destinations.MannschaftDetail.route,
arguments = listOf(navArgument("season") { nullable = true; defaultValue = null }),
) { entry ->
de.harheimertc.ui.screens.mannschaften.MannschaftDetailScreen( de.harheimertc.ui.screens.mannschaften.MannschaftDetailScreen(
slug = entry.arguments?.getString("slug").orEmpty(), slug = entry.arguments?.getString("slug").orEmpty(),
season = entry.arguments?.getString("season"),
navController = navController, navController = navController,
showBackNavigation = !persistentNavigation, showBackNavigation = !persistentNavigation,
) )
@@ -146,6 +153,7 @@ fun NavGraph(
composable(Destinations.MannschaftLegacyDetail.route) { entry -> composable(Destinations.MannschaftLegacyDetail.route) { entry ->
de.harheimertc.ui.screens.mannschaften.MannschaftDetailScreen( de.harheimertc.ui.screens.mannschaften.MannschaftDetailScreen(
slug = entry.arguments?.getString("slug").orEmpty(), slug = entry.arguments?.getString("slug").orEmpty(),
season = null,
navController = navController, navController = navController,
showBackNavigation = !persistentNavigation, showBackNavigation = !persistentNavigation,
) )

View File

@@ -85,7 +85,7 @@ fun MannschaftenScreen(
state.error != null -> item { ErrorPanel(state.error.orEmpty(), viewModel::load) } state.error != null -> item { ErrorPanel(state.error.orEmpty(), viewModel::load) }
state.teams.isEmpty() -> item { Text("Keine Mannschaftsdaten geladen", color = Accent500) } state.teams.isEmpty() -> item { Text("Keine Mannschaftsdaten geladen", color = Accent500) }
else -> items(state.teams) { team -> else -> items(state.teams) { team ->
TeamCard(team) { navController.navigate(Destinations.MannschaftDetail.create(team.slug)) } TeamCard(team) { navController.navigate(Destinations.MannschaftDetail.create(team.slug, state.selectedSeason)) }
} }
} }
item { item {
@@ -161,13 +161,14 @@ private fun TeamCard(team: Mannschaft, onOpen: () -> Unit) {
@Composable @Composable
fun MannschaftDetailScreen( fun MannschaftDetailScreen(
slug: String, slug: String,
season: String?,
navController: NavController, navController: NavController,
showBackNavigation: Boolean, showBackNavigation: Boolean,
viewModel: MannschaftDetailViewModel = hiltViewModel(), viewModel: MannschaftDetailViewModel = hiltViewModel(),
) { ) {
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
var selectedTab by rememberSaveable(slug) { mutableStateOf(DetailTab.Matches) } var selectedTab by rememberSaveable(slug, season) { mutableStateOf(DetailTab.Matches) }
LaunchedEffect(slug) { viewModel.load(slug) } LaunchedEffect(slug, season) { viewModel.load(slug, season) }
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize().background(Color(0xFFFAFAFA)), modifier = Modifier.fillMaxSize().background(Color(0xFFFAFAFA)),
contentPadding = PaddingValues(horizontal = 18.dp, vertical = 22.dp), contentPadding = PaddingValues(horizontal = 18.dp, vertical = 22.dp),
@@ -229,7 +230,7 @@ fun MannschaftDetailScreen(
} }
} }
} }
} ?: item { ErrorPanel(state.matchesError ?: "Mannschaft nicht gefunden.") { viewModel.load(slug) } } } ?: item { ErrorPanel(state.matchesError ?: "Mannschaft nicht gefunden.") { viewModel.load(slug, season) } }
} }
} }
} }

View File

@@ -121,35 +121,38 @@ class MannschaftDetailViewModel @Inject constructor(
) : ViewModel() { ) : ViewModel() {
private val _state = MutableStateFlow(MannschaftDetailUiState()) private val _state = MutableStateFlow(MannschaftDetailUiState())
val state: StateFlow<MannschaftDetailUiState> = _state val state: StateFlow<MannschaftDetailUiState> = _state
private var loadedSlug: String? = null private var loadedKey: String? = null
fun load(slug: String) { fun load(slug: String, season: String? = null) {
if (loadedSlug == slug) return val selectedSeason = season?.takeIf { it.isNotBlank() }
loadedSlug = slug val key = "$slug|${selectedSeason.orEmpty()}"
if (loadedKey == key) return
loadedKey = key
viewModelScope.launch { viewModelScope.launch {
_state.value = MannschaftDetailUiState(loading = true) _state.value = MannschaftDetailUiState(loading = true, season = selectedSeason)
val team = mannschaftenRepository.fetchMannschaften().getOrDefault(emptyList()).find { it.slug == slug } val team = mannschaftenRepository.fetchMannschaften(selectedSeason).getOrDefault(emptyList()).find { it.slug == slug }
if (team == null) { if (team == null) {
_state.value = MannschaftDetailUiState(loading = false, matchesError = "Mannschaft nicht gefunden.") _state.value = MannschaftDetailUiState(loading = false, matchesError = "Mannschaft nicht gefunden.")
return@launch return@launch
} }
spielplanRepository.fetchSpielplan() spielplanRepository.fetchSpielplan(selectedSeason)
.onSuccess { plan -> .onSuccess { plan ->
_state.value = MannschaftDetailUiState( _state.value = MannschaftDetailUiState(
loading = false, loading = false,
team = team, team = team,
matches = plan.data.filter { matchesTeam(it, team.mannschaft) }, matches = plan.data.filter { matchesTeam(it, team.mannschaft) },
season = plan.season, season = plan.season ?: selectedSeason,
) )
if (team.informationenLink.isNotBlank()) { if (team.informationenLink.isNotBlank()) {
loadTable(team, plan.season) loadTable(team, plan.season ?: selectedSeason)
} }
} }
.onFailure { .onFailure {
_state.value = MannschaftDetailUiState( _state.value = MannschaftDetailUiState(
loading = false, loading = false,
team = team, team = team,
matchesError = "Der aktuelle Spielplan konnte nicht geladen werden.", season = selectedSeason,
matchesError = "Der Spielplan konnte nicht geladen werden.",
) )
} }
} }

View File

@@ -8,8 +8,8 @@ LOCAL_API_BASE_URL=https://harheimertc.tsschulz.de/
PRODUCTION_API_BASE_URL=https://harheimertc.de/ PRODUCTION_API_BASE_URL=https://harheimertc.de/
# Android app versioning for Play Store uploads # Android app versioning for Play Store uploads
ANDROID_VERSION_CODE=24 ANDROID_VERSION_CODE=25
ANDROID_VERSION_NAME=0.9.19 ANDROID_VERSION_NAME=0.9.20
# Temporary hotfix: disable R8 minification for release to avoid Retrofit generic signature stripping. # Temporary hotfix: disable R8 minification for release to avoid Retrofit generic signature stripping.
RELEASE_MINIFY_ENABLED=false RELEASE_MINIFY_ENABLED=false