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() + ) + } } } }