diff --git a/app/src/main/java/com/radiola/ui/favorites/FavoritesScreen.kt b/app/src/main/java/com/radiola/ui/favorites/FavoritesScreen.kt index 8a8ed69..11979e9 100644 --- a/app/src/main/java/com/radiola/ui/favorites/FavoritesScreen.kt +++ b/app/src/main/java/com/radiola/ui/favorites/FavoritesScreen.kt @@ -35,6 +35,8 @@ fun FavoritesScreen( val favorites by viewModel.favorites.collectAsState() val favoriteIds by viewModel.favoriteIds.collectAsState() val nowPlaying by viewModel.nowPlaying.collectAsState() + val playingStationId by viewModel.playingStationId.collectAsState() + val isPlaying by viewModel.isPlaying.collectAsState() val colors = RadiolaTheme.colors Column( @@ -95,6 +97,8 @@ fun FavoritesScreen( onClick = { onStationClick(station) }, onFavoriteClick = { viewModel.toggleFavorite(station) }, nowTrack = nowPlaying[station.id], + isCurrent = station.id == playingStationId, + isPlaying = isPlaying, modifier = Modifier.animateItemPlacement() ) } diff --git a/app/src/main/java/com/radiola/ui/favorites/FavoritesViewModel.kt b/app/src/main/java/com/radiola/ui/favorites/FavoritesViewModel.kt index e722943..16082d4 100644 --- a/app/src/main/java/com/radiola/ui/favorites/FavoritesViewModel.kt +++ b/app/src/main/java/com/radiola/ui/favorites/FavoritesViewModel.kt @@ -9,6 +9,7 @@ import com.radiola.domain.repository.NowPlayingRepository import com.radiola.domain.usecase.ToggleFavoriteUseCase import com.radiola.domain.usecase.auth.PushFavoriteUseCase import com.radiola.domain.usecase.auth.SyncFavoritesUseCase +import com.radiola.service.PlayerController import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharingStarted @@ -24,9 +25,14 @@ class FavoritesViewModel @Inject constructor( private val toggleFavoriteUseCase: ToggleFavoriteUseCase, private val pushFavoriteUseCase: PushFavoriteUseCase, private val syncFavoritesUseCase: SyncFavoritesUseCase, - private val nowPlayingRepository: NowPlayingRepository + private val nowPlayingRepository: NowPlayingRepository, + private val playerController: PlayerController ) : ViewModel() { + // Активная (играющая) станция — для подсветки карточки, как на экране всех станций. + val playingStationId: StateFlow = playerController.currentStationId + val isPlaying: StateFlow = playerController.isPlaying + val favorites: StateFlow> = favoritesRepository.getFavorites() .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) diff --git a/app/src/main/java/com/radiola/ui/navigation/BottomNavBar.kt b/app/src/main/java/com/radiola/ui/navigation/BottomNavBar.kt index b399974..49f6d56 100644 --- a/app/src/main/java/com/radiola/ui/navigation/BottomNavBar.kt +++ b/app/src/main/java/com/radiola/ui/navigation/BottomNavBar.kt @@ -2,6 +2,9 @@ package com.radiola.ui.navigation import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween import androidx.compose.animation.expandHorizontally import androidx.compose.animation.fadeIn @@ -28,11 +31,13 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.scale import androidx.compose.ui.unit.dp import androidx.navigation.NavController import androidx.navigation.compose.currentBackStackEntryAsState @@ -43,9 +48,9 @@ import com.radiola.ui.theme.RadiolaTheme // при холодном старте может содержать null (порядок инициализации Kotlin). private val navItems = listOf( NavDestinations.Stations, - NavDestinations.Charts, NavDestinations.Favorites, NavDestinations.History, + NavDestinations.Charts, NavDestinations.Recordings, NavDestinations.Settings ) @@ -144,6 +149,13 @@ private fun VerticalPillTab( animationSpec = tween(Motion.Medium), label = "railTabFg" ) + val pop = remember { Animatable(1f) } + LaunchedEffect(selected) { + if (selected) { + pop.snapTo(0.5f) + pop.animateTo(1f, spring(dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = 620f)) + } + } Row( modifier = Modifier .size(52.dp) @@ -161,7 +173,7 @@ private fun VerticalPillTab( imageVector = icon, contentDescription = label, tint = content, - modifier = Modifier.size(22.dp) + modifier = Modifier.size(22.dp).scale(pop.value) ) } } @@ -185,6 +197,14 @@ private fun PillTab( animationSpec = tween(Motion.Medium), label = "tabFg" ) + // Упругий «поп» иконки при выборе вкладки — маленькая приятная деталь. + val pop = remember { Animatable(1f) } + LaunchedEffect(selected) { + if (selected) { + pop.snapTo(0.5f) + pop.animateTo(1f, spring(dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = 620f)) + } + } Row( modifier = modifier .fillMaxWidth() @@ -204,7 +224,7 @@ private fun PillTab( imageVector = icon, contentDescription = label, tint = content, - modifier = Modifier.height(22.dp).width(22.dp) + modifier = Modifier.height(22.dp).width(22.dp).scale(pop.value) ) } }