feat(charts): раздел «Чарты» (клиент) + детальная страница трека с графиком
- вкладка «Чарты» в навигации; экран: периоды (День/Неделя/Месяц/Всё), ранжированный список треков (ранг, обложка, проигрывания, тренд) - детальная карточка трека: метрики, график популярности (Canvas), лайк, кнопки музыкальных сервисов, кнопка «Текст песни» (ссылка на лицензированный Musixmatch — полный текст не встраиваем, авторское право) - ChartsRepository/LyricsRepository + эндпоинты charts/* в RadiolaApi (DTO) - превью-данные пока бэкенд не отдаёт charts (помечено TODO)
This commit is contained in:
26
app/src/main/java/com/radiola/domain/model/ChartEntry.kt
Normal file
26
app/src/main/java/com/radiola/domain/model/ChartEntry.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.radiola.domain.model
|
||||
|
||||
/** Период чарта, выбираемый пользователем. */
|
||||
enum class ChartPeriod(val apiValue: String, val label: String) {
|
||||
DAY("day", "День"),
|
||||
WEEK("week", "Неделя"),
|
||||
MONTH("month", "Месяц"),
|
||||
ALL("all", "Всё время")
|
||||
}
|
||||
|
||||
/** Направление движения позиции в чарте. */
|
||||
enum class ChartTrend { UP, DOWN, NEW, SAME }
|
||||
|
||||
/** Одна позиция в чарте. */
|
||||
data class ChartEntry(
|
||||
val rank: Int,
|
||||
val trackId: String,
|
||||
val artist: String,
|
||||
val song: String,
|
||||
val coverUrl: String?,
|
||||
val plays: Int,
|
||||
val stationsCount: Int,
|
||||
val likes: Int,
|
||||
val prevRank: Int?,
|
||||
val trend: ChartTrend
|
||||
)
|
||||
34
app/src/main/java/com/radiola/domain/model/TrackStats.kt
Normal file
34
app/src/main/java/com/radiola/domain/model/TrackStats.kt
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.radiola.domain.model
|
||||
|
||||
/** Одна точка на графике популярности. */
|
||||
data class StatPoint(
|
||||
/** Метка времени в epoch-миллисекундах. */
|
||||
val date: Long,
|
||||
val value: Int
|
||||
)
|
||||
|
||||
/** Проигрывания на конкретной станции. */
|
||||
data class StationPlays(
|
||||
val stationId: Int,
|
||||
val name: String,
|
||||
val plays: Int
|
||||
)
|
||||
|
||||
/** Полная статистика трека (детальная карточка). */
|
||||
data class TrackStats(
|
||||
val trackId: String,
|
||||
val artist: String,
|
||||
val song: String,
|
||||
val album: String?,
|
||||
val coverUrl: String?,
|
||||
val releaseDate: String?,
|
||||
val firstSeen: String?,
|
||||
val totalPlays: Int,
|
||||
val totalLikes: Int,
|
||||
val isLiked: Boolean,
|
||||
val currentRank: Int?,
|
||||
val peakRank: Int?,
|
||||
val stations: List<StationPlays>,
|
||||
val playsTimeline: List<StatPoint>,
|
||||
val likesTimeline: List<StatPoint>
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.radiola.domain.repository
|
||||
|
||||
import com.radiola.domain.model.ChartEntry
|
||||
import com.radiola.domain.model.ChartPeriod
|
||||
import com.radiola.domain.model.TrackStats
|
||||
|
||||
interface ChartsRepository {
|
||||
suspend fun getCharts(period: ChartPeriod): List<ChartEntry>
|
||||
suspend fun getTrackStats(trackId: String): TrackStats
|
||||
suspend fun setLiked(trackId: String, liked: Boolean)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.radiola.domain.repository
|
||||
|
||||
// Тексты песен — авторское право. Показываем ссылку на лицензированный сервис,
|
||||
// полный текст не храним/не встраиваем.
|
||||
// Для сниппета подключить официальный Musixmatch API (с атрибуцией).
|
||||
interface LyricsRepository {
|
||||
/** URL поиска на лицензированном сервисе Musixmatch. */
|
||||
fun providerUrl(artist: String, song: String): String
|
||||
|
||||
/**
|
||||
* Лицензированный сниппет текста.
|
||||
* TODO: подключить официальный Musixmatch API (с атрибуцией) и вернуть реальный сниппет.
|
||||
*/
|
||||
suspend fun snippet(artist: String, song: String): String? = null
|
||||
}
|
||||
Reference in New Issue
Block a user