fix(stations): обложки Record только для Record-станций + своя плитка остальным

- сети, отличные от Radio Record (DFM, HitFM и др.), больше не получают
  обложки Radio Record (обогащение Record API гейтится по source=record)
- станции без обложки рисуют свою фирменную плитку: цвет по названию + инициалы
  (вместо общего значка/чужой обложки)
This commit is contained in:
nk
2026-06-03 11:36:24 +03:00
parent 32e5108d98
commit 5fd97d27fd
3 changed files with 49 additions and 12 deletions

View File

@@ -29,16 +29,19 @@ class LocalStationDataSource @Inject constructor(
.map { dto -> .map { dto ->
val group = groupMap[dto.groupId] val group = groupMap[dto.groupId]
val prefix = generatePrefix(dto.name) val prefix = generatePrefix(dto.name)
// Определяем сеть: только станции Radio Record можно обогащать
// обложками из Record API. Остальные сети — свой источник.
val isRecord = dto.site?.contains("radiorecord", ignoreCase = true) == true
Station( Station(
id = dto.id, id = dto.id,
name = dto.name, name = dto.name,
prefix = prefix, prefix = prefix,
streamUrl = dto.stream!!, streamUrl = dto.stream!!,
coverUrl = group?.let { generateCoverUrl(it.name, dto.name) } ?: "", coverUrl = "",
genre = group?.name ?: "", genre = group?.name ?: "",
tags = listOfNotNull(group?.name?.takeIf { it.isNotBlank() }), tags = listOfNotNull(group?.name?.takeIf { it.isNotBlank() }),
sortOrder = dto.id, sortOrder = dto.id,
source = "local" source = if (isRecord) "record" else "local"
) )
} }
} }

View File

@@ -46,10 +46,14 @@ class StationRepositoryImpl @Inject constructor(
// нет prefix — поэтому сопоставляем сначала по id, затем по названию // нет prefix — поэтому сопоставляем сначала по id, затем по названию
// (стабильный общий ключ), иначе обложки/потоки не подтягиваются. // (стабильный общий ключ), иначе обложки/потоки не подтягиваются.
val merged = localStations.map { local -> val merged = localStations.map { local ->
val apiStation = apiStations.find { it.id == local.id } // Обложки/потоки из Record API — только для станций сети Radio Record.
?: apiStations.find { // Иначе чужим сетям (DFM, HitFM и т.д.) цеплялись бы обложки Record.
it.name.trim().equals(local.name.trim(), ignoreCase = true) val apiStation = if (local.source == "record") {
} apiStations.find { it.id == local.id }
?: apiStations.find {
it.name.trim().equals(local.name.trim(), ignoreCase = true)
}
} else null
if (apiStation != null) { if (apiStation != null) {
val domain = apiStation.toDomain() val domain = apiStation.toDomain()
local.copy( local.copy(

View File

@@ -15,6 +15,8 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
@@ -66,12 +68,21 @@ fun StationCard(
contentScale = ContentScale.Crop contentScale = ContentScale.Crop
) )
} else { } else {
Icon( // Своя фирменная плитка станции (цвет из названия + инициалы),
Lucide.Radio, // а не общий значок и не чужая обложка.
contentDescription = null, Box(
tint = colors.textMuted, modifier = Modifier
modifier = Modifier.align(Alignment.Center).size(34.dp) .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
)
}
} }
Box( Box(
modifier = Modifier modifier = Modifier
@@ -114,3 +125,22 @@ fun StationCard(
} }
} }
} }
/** Инициалы станции для плитки-плейсхолдера (12 символа). */
private fun stationInitials(name: String): String {
val words = name.trim().split(Regex("\\s+")).filter { it.isNotBlank() }
return when {
words.isEmpty() -> "?"
words.size == 1 -> words[0].take(2).uppercase()
else -> (words[0].take(1) + words[1].take(1)).uppercase()
}
}
/** Детерминированный фирменный градиент плитки по названию станции. */
private fun stationTileBrush(name: String): Brush {
val h = (name.hashCode().toLong() and 0xFFFFFFFFL)
val hue = (h % 360L).toFloat()
val c1 = Color.hsv(hue, 0.55f, 0.45f)
val c2 = Color.hsv((hue + 28f) % 360f, 0.6f, 0.30f)
return Brush.linearGradient(listOf(c1, c2))
}