feat(stations): обложка текущего трека на карточке станции + подпись

Для станций без своей обложки (и для Radio Record — единый стиль) карточка
показывает обложку играющего трека с тёмным градиентом и подписью трек/исполнитель.
Источник — /now-playing (теперь с name станции), матч по имени, обновление 20с.
Приоритет: трек -> логотип станции -> фирменная плитка.
This commit is contained in:
nk
2026-06-03 12:18:19 +03:00
parent 9d115b148e
commit ee689ce380
8 changed files with 129 additions and 27 deletions

View File

@@ -25,8 +25,8 @@ import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.composables.icons.lucide.Heart
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Radio
import com.radiola.domain.model.Station
import com.radiola.domain.model.Track
import com.radiola.ui.theme.Motion
import com.radiola.ui.theme.RadiolaTheme
import com.radiola.ui.theme.pressScale
@@ -37,7 +37,8 @@ fun StationCard(
isFavorite: Boolean,
onClick: () -> Unit,
onFavoriteClick: () -> Unit,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
nowTrack: Track? = null
) {
val colors = RadiolaTheme.colors
val haptics = LocalHapticFeedback.current
@@ -60,37 +61,84 @@ fun StationCard(
.clip(RoundedCornerShape(16.dp))
.background(colors.surface2)
) {
if (!station.coverUrl.isNullOrBlank()) {
AsyncImage(
model = crossfadeModel(station.coverUrl),
contentDescription = station.name,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
} else {
// Своя фирменная плитка станции (цвет из названия + инициалы),
// а не общий значок и не чужая обложка.
Box(
modifier = Modifier
.fillMaxSize()
.background(stationTileBrush(station.name)),
contentAlignment = Alignment.Center
) {
Text(
text = stationInitials(station.name),
color = androidx.compose.ui.graphics.Color.White,
fontWeight = FontWeight.Black,
style = androidx.compose.material3.MaterialTheme.typography.headlineMedium
when {
// Приоритет 1: обложка текущего трека с градиентом и подписью.
!nowTrack?.coverUrl.isNullOrBlank() -> {
AsyncImage(
model = crossfadeModel(nowTrack!!.coverUrl),
contentDescription = nowTrack.song,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
// Тёмный скрим снизу для читаемости текста.
Box(
modifier = Modifier
.fillMaxSize()
.background(
Brush.verticalGradient(
0f to Color.Transparent,
0.5f to Color.Transparent,
1f to Color.Black.copy(alpha = 0.8f)
)
)
)
// Название трека и исполнитель в нижнем-левом углу.
Column(
modifier = Modifier
.align(Alignment.BottomStart)
.padding(10.dp)
) {
Text(
text = nowTrack.song,
color = Color.White,
fontWeight = FontWeight.Bold,
style = androidx.compose.material3.MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
)
Text(
text = nowTrack.artist,
color = Color.White.copy(alpha = 0.8f),
style = androidx.compose.material3.MaterialTheme.typography.labelMedium,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
)
}
}
// Приоритет 2: логотип самой станции.
!station.coverUrl.isNullOrBlank() -> {
AsyncImage(
model = crossfadeModel(station.coverUrl),
contentDescription = station.name,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
// Приоритет 3: фирменная плитка (цвет из названия + инициалы).
else -> {
Box(
modifier = Modifier
.fillMaxSize()
.background(stationTileBrush(station.name)),
contentAlignment = Alignment.Center
) {
Text(
text = stationInitials(station.name),
color = Color.White,
fontWeight = FontWeight.Black,
style = androidx.compose.material3.MaterialTheme.typography.headlineMedium
)
}
}
}
// Кнопка сердечка — поверх всего, top-end.
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(10.dp)
.size(32.dp)
.clip(RoundedCornerShape(16.dp))
.background(androidx.compose.ui.graphics.Color.Black.copy(alpha = 0.4f))
.background(Color.Black.copy(alpha = 0.4f))
.clickable {
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
onFavoriteClick()