From 9e9f4c80094faf8582890ab56faa296fbeff4765 Mon Sep 17 00:00:00 2001 From: nk Date: Tue, 2 Jun 2026 21:58:11 +0300 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=D0=B5=D0=B4=D0=B8=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20=D1=81=D0=BA=D1=80=D0=BE=D0=BB=D0=BB=20=D0=BD=D0=B0=20=D1=8D?= =?UTF-8?q?=D0=BA=D1=80=D0=B0=D0=BD=D0=B5=20=D1=81=D1=82=D0=B0=D0=BD=D1=86?= =?UTF-8?q?=D0=B8=D0=B9=20+=20=D0=B2=D1=81=D0=B5=D0=B3=D0=B4=D0=B0=20?= =?UTF-8?q?=D0=B2=D0=B8=D0=B4=D0=B8=D0=BC=D1=8B=D0=B9=20=D0=BD=D0=B0=D0=B2?= =?UTF-8?q?=D0=B1=D0=B0=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - StationsScreen: закреплённые заголовок/поиск/жанры, одна прокручиваемая сетка станций; поиск и фильтры больше не исчезают при пустом результате (+ кнопка «Сбросить фильтры») - таб-бар показывается без обязательного входа (скрыт только на экране входа) - старт сразу со «Станций» — авторизация необязательна, вход из Настроек --- app/src/main/java/com/radiola/MainActivity.kt | 37 ++++-- .../com/radiola/ui/stations/StationsScreen.kt | 124 +++++++++--------- 2 files changed, 87 insertions(+), 74 deletions(-) diff --git a/app/src/main/java/com/radiola/MainActivity.kt b/app/src/main/java/com/radiola/MainActivity.kt index fd2598a..61de425 100644 --- a/app/src/main/java/com/radiola/MainActivity.kt +++ b/app/src/main/java/com/radiola/MainActivity.kt @@ -9,9 +9,11 @@ import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.lifecycle.lifecycleScope import com.radiola.data.local.TokenDataStore @@ -65,23 +67,30 @@ class MainActivity : ComponentActivity() { val isRecording by playerViewModel.isRecording.collectAsState() val isLoggedIn by tokenDataStore.isLoggedIn.collectAsState(initial = false) - val startDestination = remember(isLoggedIn) { - if (isLoggedIn) NavDestinations.Stations.route else NavDestinations.Auth.route - } + // Авторизация необязательна — всегда стартуем со станций. + // Вход доступен из Настроек. + val startDestination = NavDestinations.Stations.route + + val currentRoute = navController + .currentBackStackEntryAsState().value?.destination?.route + val showChrome = currentRoute != NavDestinations.Auth.route Scaffold( bottomBar = { - Column { - if (currentStation != null) { - MiniPlayer( - stationName = currentStation!!.name, - track = currentTrack, - isPlaying = isPlaying, - onClick = { showPlayer = true }, - onPlayPause = { playerViewModel.togglePlayPause() } - ) - } - if (isLoggedIn) { + if (showChrome) { + Column { + if (currentStation != null) { + MiniPlayer( + stationName = currentStation!!.name, + track = currentTrack, + isPlaying = isPlaying, + onClick = { showPlayer = true }, + onPlayPause = { playerViewModel.togglePlayPause() } + ) + Spacer(Modifier.height(8.dp)) + } + // Навигация доступна и без входа — приложением можно + // пользоваться анонимно. BottomNavBar(navController) } } diff --git a/app/src/main/java/com/radiola/ui/stations/StationsScreen.kt b/app/src/main/java/com/radiola/ui/stations/StationsScreen.kt index 354860c..b20dee4 100644 --- a/app/src/main/java/com/radiola/ui/stations/StationsScreen.kt +++ b/app/src/main/java/com/radiola/ui/stations/StationsScreen.kt @@ -1,12 +1,8 @@ package com.radiola.ui.stations -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.slideInVertically import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.material3.* @@ -18,6 +14,8 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import com.composables.icons.lucide.Lucide +import com.composables.icons.lucide.Radio import com.radiola.domain.model.Station import com.radiola.ui.components.* import com.radiola.ui.theme.RadiolaTheme @@ -38,11 +36,7 @@ fun StationsScreen( val favoriteIds by viewModel.favoriteIds.collectAsState() val colors = RadiolaTheme.colors - Column( - modifier = modifier - .fillMaxSize() - .padding(horizontal = 20.dp) - ) { + Column(modifier = modifier.fillMaxSize()) { // Двухцветный заголовок экрана Text( text = buildAnnotatedString { @@ -50,72 +44,82 @@ fun StationsScreen( withStyle(SpanStyle(color = colors.accent)) { append("радио") } }, style = MaterialTheme.typography.headlineLarge, - modifier = Modifier.padding(top = 20.dp, bottom = 16.dp) + modifier = Modifier.padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 16.dp) ) - when { - isLoading && stations.isEmpty() -> Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - CircularProgressIndicator(color = colors.accent) - } + // Поиск — всегда виден (в т.ч. когда результатов нет) + SearchBar( + query = searchQuery, + onQueryChange = viewModel::onSearchQueryChange, + modifier = Modifier.padding(horizontal = 20.dp) + ) + Spacer(Modifier.height(12.dp)) - stations.isEmpty() -> { - AnimatedVisibility( - visible = true, - enter = fadeIn() + slideInVertically() - ) { + // Жанры — всегда видны + if (tags.isNotEmpty()) { + FilterChips( + tags = tags, + selectedTag = selectedTag, + onTagSelected = viewModel::onTagSelected + ) + Spacer(Modifier.height(8.dp)) + } + + // Область результатов — единственная прокручиваемая зона + Box(modifier = Modifier.weight(1f).fillMaxWidth()) { + when { + isLoading && stations.isEmpty() -> { + CircularProgressIndicator( + color = colors.accent, + modifier = Modifier.align(Alignment.Center) + ) + } + + stations.isEmpty() -> { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(12.dp) + verticalArrangement = Arrangement.Center ) { - EmptyState(message = error ?: "Станции не найдены") - if (selectedTag != null) { + EmptyState( + message = error + ?: if (searchQuery.isNotBlank() || selectedTag != null) + "Ничего не найдено" else "Станции не найдены", + icon = Lucide.Radio, + modifier = Modifier.wrapContentSize() + ) + if (searchQuery.isNotBlank() || selectedTag != null) { + Spacer(Modifier.height(12.dp)) OutlinedButton( - onClick = { viewModel.onTagSelected(null) }, - colors = ButtonDefaults.outlinedButtonColors( - contentColor = colors.accent - ), + onClick = { + viewModel.onSearchQueryChange("") + viewModel.onTagSelected(null) + }, + colors = ButtonDefaults.outlinedButtonColors(contentColor = colors.accent), border = androidx.compose.foundation.BorderStroke(1.dp, colors.accent) ) { - Text("Показать все") + Text("Сбросить фильтры") } } } } - } - else -> LazyVerticalGrid( - columns = GridCells.Fixed(2), - modifier = Modifier.fillMaxSize(), - contentPadding = PaddingValues(bottom = 16.dp), - horizontalArrangement = Arrangement.spacedBy(14.dp), - verticalArrangement = Arrangement.spacedBy(14.dp) - ) { - item(span = { GridItemSpan(maxLineSpan) }) { - SearchBar( - query = searchQuery, - onQueryChange = viewModel::onSearchQueryChange, - ) - } - item(span = { GridItemSpan(maxLineSpan) }) { - FilterChips( - tags = tags, - selectedTag = selectedTag, - onTagSelected = viewModel::onTagSelected, - modifier = Modifier.padding(vertical = 8.dp) - ) - } - items(stations, key = { it.id }) { station -> - StationCard( - station = station, - isFavorite = favoriteIds.contains(station.id), - onClick = { onStationClick(station) }, - onFavoriteClick = { viewModel.toggleFavorite(station) }, - modifier = Modifier.animateItemPlacement() - ) + else -> LazyVerticalGrid( + columns = GridCells.Fixed(2), + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues(start = 20.dp, end = 20.dp, top = 4.dp, bottom = 20.dp), + horizontalArrangement = Arrangement.spacedBy(14.dp), + verticalArrangement = Arrangement.spacedBy(14.dp) + ) { + items(stations, key = { it.id }) { station -> + StationCard( + station = station, + isFavorite = favoriteIds.contains(station.id), + onClick = { onStationClick(station) }, + onFavoriteClick = { viewModel.toggleFavorite(station) }, + modifier = Modifier.animateItemPlacement() + ) + } } } }