mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 07:29:14 +01:00
home: drop On repeat, restore starred, give artist lists the detail column
This commit is contained in:
@@ -304,18 +304,6 @@ interface LibraryDao {
|
|||||||
limit: Int,
|
limit: Int,
|
||||||
): Flow<List<AlbumSongItem>>
|
): Flow<List<AlbumSongItem>>
|
||||||
|
|
||||||
@Query(
|
|
||||||
"SELECT songs.*, albums.coverArt AS coverArt FROM songs " +
|
|
||||||
"LEFT JOIN albums ON albums.sourceId = songs.sourceId AND albums.id = songs.albumId " +
|
|
||||||
"WHERE songs.sourceId = :sourceId AND songs.played IS NOT NULL AND songs.played >= :cutoff " +
|
|
||||||
"ORDER BY songs.playCount DESC, songs.title COLLATE NOCASE, songs.id LIMIT :limit",
|
|
||||||
)
|
|
||||||
fun onRepeatSongs(
|
|
||||||
sourceId: Long,
|
|
||||||
cutoff: Long,
|
|
||||||
limit: Int,
|
|
||||||
): Flow<List<AlbumSongItem>>
|
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"SELECT genre FROM (" +
|
"SELECT genre FROM (" +
|
||||||
"SELECT song_genres.genre AS genre, songs.playCount AS plays FROM song_genres " +
|
"SELECT song_genres.genre AS genre, songs.playCount AS plays FROM song_genres " +
|
||||||
@@ -722,26 +710,6 @@ interface LibraryDao {
|
|||||||
)
|
)
|
||||||
suspend fun starredSongIds(sourceId: Long): List<String>
|
suspend fun starredSongIds(sourceId: Long): List<String>
|
||||||
|
|
||||||
@Query(
|
|
||||||
"SELECT songs.*, albums.coverArt AS coverArt FROM songs " +
|
|
||||||
"LEFT JOIN albums ON albums.sourceId = songs.sourceId AND albums.id = songs.albumId " +
|
|
||||||
"WHERE songs.sourceId = :sourceId AND songs.played IS NOT NULL AND songs.played >= :cutoff " +
|
|
||||||
"ORDER BY songs.playCount DESC, songs.title COLLATE NOCASE, songs.id",
|
|
||||||
)
|
|
||||||
fun homeOnRepeatSongs(
|
|
||||||
sourceId: Long,
|
|
||||||
cutoff: Long,
|
|
||||||
): PagingSource<Int, AlbumSongItem>
|
|
||||||
|
|
||||||
@Query(
|
|
||||||
"SELECT songs.id FROM songs WHERE sourceId = :sourceId AND played IS NOT NULL AND played >= :cutoff " +
|
|
||||||
"ORDER BY playCount DESC, title COLLATE NOCASE, id",
|
|
||||||
)
|
|
||||||
suspend fun onRepeatSongIds(
|
|
||||||
sourceId: Long,
|
|
||||||
cutoff: Long,
|
|
||||||
): List<String>
|
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"SELECT songs.*, albums.coverArt AS coverArt FROM songs " +
|
"SELECT songs.*, albums.coverArt AS coverArt FROM songs " +
|
||||||
"LEFT JOIN albums ON albums.sourceId = songs.sourceId AND albums.id = songs.albumId " +
|
"LEFT JOIN albums ON albums.sourceId = songs.sourceId AND albums.id = songs.albumId " +
|
||||||
|
|||||||
@@ -90,16 +90,6 @@ class LibraryRepository(
|
|||||||
|
|
||||||
suspend fun starredSongIds(sourceId: Long): List<String> = db.libraryDao().starredSongIds(sourceId)
|
suspend fun starredSongIds(sourceId: Long): List<String> = db.libraryDao().starredSongIds(sourceId)
|
||||||
|
|
||||||
fun onRepeatSongs(
|
|
||||||
sourceId: Long,
|
|
||||||
limit: Int,
|
|
||||||
): Flow<List<AlbumSongItem>> = db.libraryDao().onRepeatSongs(sourceId, weekAgoCutoff(), limit)
|
|
||||||
|
|
||||||
fun onRepeatSongsPage(sourceId: Long): Flow<PagingData<AlbumSongItem>> =
|
|
||||||
pager(60) { db.libraryDao().homeOnRepeatSongs(sourceId, weekAgoCutoff()) }
|
|
||||||
|
|
||||||
suspend fun onRepeatSongIds(sourceId: Long): List<String> = db.libraryDao().onRepeatSongIds(sourceId, weekAgoCutoff())
|
|
||||||
|
|
||||||
fun songsByGenre(
|
fun songsByGenre(
|
||||||
sourceId: Long,
|
sourceId: Long,
|
||||||
genre: String,
|
genre: String,
|
||||||
@@ -448,5 +438,3 @@ class LibraryRepository(
|
|||||||
const val REDISCOVER_WINDOW_MS = 60L * 24 * 60 * 60 * 1000
|
const val REDISCOVER_WINDOW_MS = 60L * 24 * 60 * 60 * 1000
|
||||||
|
|
||||||
fun rediscoverCutoff(): Long = System.currentTimeMillis() - REDISCOVER_WINDOW_MS
|
fun rediscoverCutoff(): Long = System.currentTimeMillis() - REDISCOVER_WINDOW_MS
|
||||||
|
|
||||||
fun weekAgoCutoff(): Long = System.currentTimeMillis() / 1000 - 7L * 24 * 60 * 60
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||||
@@ -52,7 +53,6 @@ import com.subtracks.ui.components.CoverArt
|
|||||||
import com.subtracks.ui.components.ItemActions
|
import com.subtracks.ui.components.ItemActions
|
||||||
import com.subtracks.ui.components.LoadingState
|
import com.subtracks.ui.components.LoadingState
|
||||||
import com.subtracks.ui.components.MenuTarget
|
import com.subtracks.ui.components.MenuTarget
|
||||||
import com.subtracks.ui.library.ArtistsContent
|
|
||||||
import com.subtracks.ui.library.SongRow
|
import com.subtracks.ui.library.SongRow
|
||||||
import org.koin.compose.koinInject
|
import org.koin.compose.koinInject
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
@@ -159,17 +159,17 @@ fun HomeListScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
HomeSection.RecentlyPlayedArtists, HomeSection.MostPlayedArtists -> {
|
HomeSection.RecentlyPlayedArtists, HomeSection.MostPlayedArtists -> {
|
||||||
ArtistsContent(
|
ArtistsList(
|
||||||
items = artists,
|
artists = artists,
|
||||||
coverArt = coverArt,
|
coverArt = coverArt,
|
||||||
bottomInset = 0.dp,
|
info = { artistListInfo(request, it, now) },
|
||||||
onArtistClick = onArtistClick,
|
onArtistClick = onArtistClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
modifier = Modifier.padding(padding),
|
modifier = Modifier.padding(padding),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
HomeSection.RecentlyStarredSongs, HomeSection.OnRepeatSongs -> {
|
HomeSection.RecentlyStarredSongs -> {
|
||||||
SongsList(
|
SongsList(
|
||||||
songs = songs,
|
songs = songs,
|
||||||
coverArt = coverArt,
|
coverArt = coverArt,
|
||||||
@@ -351,6 +351,100 @@ private fun AlbumListRow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ArtistsList(
|
||||||
|
artists: LazyPagingItems<Artist>,
|
||||||
|
coverArt: (String?, Boolean) -> CoverArtRef?,
|
||||||
|
info: (Artist) -> String?,
|
||||||
|
onArtistClick: (Artist) -> Unit,
|
||||||
|
onLongClick: (MenuTarget) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
if (artists.itemCount == 0 && artists.loadState.refresh is LoadState.Loading) {
|
||||||
|
LoadingState(modifier)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
LazyColumn(
|
||||||
|
modifier = modifier.fillMaxSize(),
|
||||||
|
contentPadding = PaddingValues(bottom = 24.dp),
|
||||||
|
) {
|
||||||
|
items(count = artists.itemCount, key = artists.itemKey { it.id }) { index ->
|
||||||
|
val artist = artists[index]
|
||||||
|
if (artist != null) {
|
||||||
|
ArtistListRow(
|
||||||
|
artist = artist,
|
||||||
|
coverArt = coverArt,
|
||||||
|
info = info(artist),
|
||||||
|
onClick = { onArtistClick(artist) },
|
||||||
|
onLongClick = { onLongClick(MenuTarget.Artist(artist, coverArt(artist.coverArt, true))) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ArtistListRow(
|
||||||
|
artist: Artist,
|
||||||
|
coverArt: (String?, Boolean) -> CoverArtRef?,
|
||||||
|
info: String?,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onLongClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.combinedClickable(onClick = onClick, onLongClick = onLongClick)
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
CoverArt(
|
||||||
|
ref = coverArt(artist.coverArt, true),
|
||||||
|
name = artist.name,
|
||||||
|
modifier = Modifier.size(ALBUM_ROW_COVER).clip(CircleShape),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(12.dp))
|
||||||
|
Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||||
|
Text(
|
||||||
|
text = artist.name,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(
|
||||||
|
text = "${artist.albumCount} ${if (artist.albumCount == 1L) "album" else "albums"}",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
if (info != null) {
|
||||||
|
Text(
|
||||||
|
text = info,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
modifier = Modifier.padding(start = 8.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun artistListInfo(
|
||||||
|
request: HomeListRequest,
|
||||||
|
artist: Artist,
|
||||||
|
now: Long,
|
||||||
|
): String? =
|
||||||
|
when (request.section) {
|
||||||
|
HomeSection.RecentlyPlayedArtists -> artist.played?.let { relativeTimeLabel(it, now) }
|
||||||
|
HomeSection.MostPlayedArtists -> playCountLabel(artist.playCount)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
internal fun albumListInfo(
|
internal fun albumListInfo(
|
||||||
request: HomeListRequest,
|
request: HomeListRequest,
|
||||||
album: Album,
|
album: Album,
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ class HomeListViewModel(
|
|||||||
when {
|
when {
|
||||||
request.genre != null -> libraryRepository.songsByGenre(id, request.genre)
|
request.genre != null -> libraryRepository.songsByGenre(id, request.genre)
|
||||||
request.section == HomeSection.RecentlyStarredSongs -> libraryRepository.starredSongs(id)
|
request.section == HomeSection.RecentlyStarredSongs -> libraryRepository.starredSongs(id)
|
||||||
request.section == HomeSection.OnRepeatSongs -> libraryRepository.onRepeatSongsPage(id)
|
|
||||||
else -> flowOf(PagingData.empty<AlbumSongItem>())
|
else -> flowOf(PagingData.empty<AlbumSongItem>())
|
||||||
}
|
}
|
||||||
}.cachedIn(viewModelScope)
|
}.cachedIn(viewModelScope)
|
||||||
@@ -112,10 +111,10 @@ class HomeListViewModel(
|
|||||||
): CoverArtRef? = sourceRepository.coverArt(coverArt, thumbnail)
|
): CoverArtRef? = sourceRepository.coverArt(coverArt, thumbnail)
|
||||||
|
|
||||||
fun play(song: Song) {
|
fun play(song: Song) {
|
||||||
when (request.section) {
|
if (request.section == HomeSection.RecentlyStarredSongs) {
|
||||||
HomeSection.RecentlyStarredSongs -> viewModelScope.playStarredList(libraryRepository, playbackController, song)
|
viewModelScope.playStarredList(libraryRepository, playbackController, song)
|
||||||
HomeSection.OnRepeatSongs -> viewModelScope.playOnRepeatList(libraryRepository, playbackController, song)
|
} else {
|
||||||
else -> viewModelScope.playSongInContext(libraryRepository, playbackController, song)
|
viewModelScope.playSongInContext(libraryRepository, playbackController, song)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ enum class HomeSection(
|
|||||||
Genres("Genres"),
|
Genres("Genres"),
|
||||||
Decades("Decades"),
|
Decades("Decades"),
|
||||||
RecentlyStarredSongs("Recently starred"),
|
RecentlyStarredSongs("Recently starred"),
|
||||||
OnRepeatSongs("On repeat"),
|
|
||||||
RecentlyAddedAlbums("Recently added"),
|
RecentlyAddedAlbums("Recently added"),
|
||||||
Rediscover("Rediscover"),
|
Rediscover("Rediscover"),
|
||||||
;
|
;
|
||||||
@@ -110,7 +109,6 @@ fun HomeRoute(
|
|||||||
onAlbumClick = onAlbumClick,
|
onAlbumClick = onAlbumClick,
|
||||||
onArtistClick = onArtistClick,
|
onArtistClick = onArtistClick,
|
||||||
onPlayStarred = viewModel::playStarred,
|
onPlayStarred = viewModel::playStarred,
|
||||||
onPlayOnRepeat = viewModel::playOnRepeat,
|
|
||||||
onLongClick = { contextMenuHost?.show(it, actions) },
|
onLongClick = { contextMenuHost?.show(it, actions) },
|
||||||
onMore = onMore,
|
onMore = onMore,
|
||||||
onGenreClick = onGenreClick,
|
onGenreClick = onGenreClick,
|
||||||
@@ -129,7 +127,6 @@ fun HomeScreen(
|
|||||||
onAlbumClick: (Album) -> Unit = {},
|
onAlbumClick: (Album) -> Unit = {},
|
||||||
onArtistClick: (Artist) -> Unit = {},
|
onArtistClick: (Artist) -> Unit = {},
|
||||||
onPlayStarred: (Song) -> Unit = {},
|
onPlayStarred: (Song) -> Unit = {},
|
||||||
onPlayOnRepeat: (Song) -> Unit = {},
|
|
||||||
onLongClick: (MenuTarget) -> Unit = {},
|
onLongClick: (MenuTarget) -> Unit = {},
|
||||||
onMore: (HomeSection) -> Unit = {},
|
onMore: (HomeSection) -> Unit = {},
|
||||||
onGenreClick: (String) -> Unit = {},
|
onGenreClick: (String) -> Unit = {},
|
||||||
@@ -190,11 +187,11 @@ fun HomeScreen(
|
|||||||
onMore,
|
onMore,
|
||||||
)
|
)
|
||||||
homeSongs(
|
homeSongs(
|
||||||
HomeSection.OnRepeatSongs,
|
HomeSection.RecentlyStarredSongs,
|
||||||
feed.onRepeatSongs,
|
feed.recentlyStarredSongs,
|
||||||
coverArt,
|
coverArt,
|
||||||
playingSongId,
|
playingSongId,
|
||||||
onPlayOnRepeat,
|
onPlayStarred,
|
||||||
onLongClick,
|
onLongClick,
|
||||||
onMore,
|
onMore,
|
||||||
)
|
)
|
||||||
@@ -207,12 +204,11 @@ fun HomeScreen(
|
|||||||
onMore,
|
onMore,
|
||||||
)
|
)
|
||||||
genreBlock(HomeSection.Genres, feed.genres, onGenreClick, onMore)
|
genreBlock(HomeSection.Genres, feed.genres, onGenreClick, onMore)
|
||||||
homeSongs(
|
albumRow(
|
||||||
HomeSection.RecentlyStarredSongs,
|
HomeSection.RecentlyPlayedAlbums,
|
||||||
feed.recentlyStarredSongs,
|
feed.recentlyPlayedAlbums,
|
||||||
coverArt,
|
coverArt,
|
||||||
playingSongId,
|
onAlbumClick,
|
||||||
onPlayStarred,
|
|
||||||
onLongClick,
|
onLongClick,
|
||||||
onMore,
|
onMore,
|
||||||
)
|
)
|
||||||
@@ -233,14 +229,6 @@ fun HomeScreen(
|
|||||||
onLongClick,
|
onLongClick,
|
||||||
onMore,
|
onMore,
|
||||||
)
|
)
|
||||||
albumRow(
|
|
||||||
HomeSection.RecentlyPlayedAlbums,
|
|
||||||
feed.recentlyPlayedAlbums,
|
|
||||||
coverArt,
|
|
||||||
onAlbumClick,
|
|
||||||
onLongClick,
|
|
||||||
onMore,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ data class HomeFeed(
|
|||||||
val genres: List<String> = emptyList(),
|
val genres: List<String> = emptyList(),
|
||||||
val decades: List<Long> = emptyList(),
|
val decades: List<Long> = emptyList(),
|
||||||
val recentlyStarredSongs: List<AlbumSongItem> = emptyList(),
|
val recentlyStarredSongs: List<AlbumSongItem> = emptyList(),
|
||||||
val onRepeatSongs: List<AlbumSongItem> = emptyList(),
|
|
||||||
val recentlyAddedAlbums: List<Album> = emptyList(),
|
val recentlyAddedAlbums: List<Album> = emptyList(),
|
||||||
val rediscoverAlbums: List<Album> = emptyList(),
|
val rediscoverAlbums: List<Album> = emptyList(),
|
||||||
val loading: Boolean = false,
|
val loading: Boolean = false,
|
||||||
@@ -63,9 +62,8 @@ class HomeViewModel(
|
|||||||
libraryRepository.recentlyPlayedArtists(sourceId, HOME_ROW_LIMIT),
|
libraryRepository.recentlyPlayedArtists(sourceId, HOME_ROW_LIMIT),
|
||||||
libraryRepository.mostPlayedAlbums(sourceId, HOME_ROW_LIMIT),
|
libraryRepository.mostPlayedAlbums(sourceId, HOME_ROW_LIMIT),
|
||||||
libraryRepository.mostPlayedArtists(sourceId, HOME_ROW_LIMIT),
|
libraryRepository.mostPlayedArtists(sourceId, HOME_ROW_LIMIT),
|
||||||
libraryRepository.onRepeatSongs(sourceId, HOME_STARRED_LIMIT),
|
) { albums, artists, frequentAlbums, frequentArtists ->
|
||||||
) { albums, artists, frequentAlbums, frequentArtists, onRepeat ->
|
PlayRows(albums, artists, frequentAlbums, frequentArtists)
|
||||||
PlayRows(albums, artists, frequentAlbums, frequentArtists, onRepeat)
|
|
||||||
}
|
}
|
||||||
val discovery =
|
val discovery =
|
||||||
combine(
|
combine(
|
||||||
@@ -88,7 +86,6 @@ class HomeViewModel(
|
|||||||
recentArtists = rows.recentArtists,
|
recentArtists = rows.recentArtists,
|
||||||
frequentAlbums = rows.frequentAlbums,
|
frequentAlbums = rows.frequentAlbums,
|
||||||
frequentArtists = rows.frequentArtists,
|
frequentArtists = rows.frequentArtists,
|
||||||
onRepeatSongs = rows.onRepeatSongs,
|
|
||||||
genres = found.genres,
|
genres = found.genres,
|
||||||
decades = found.decades,
|
decades = found.decades,
|
||||||
starredSongs = found.starredSongs,
|
starredSongs = found.starredSongs,
|
||||||
@@ -108,10 +105,6 @@ class HomeViewModel(
|
|||||||
viewModelScope.playStarredList(libraryRepository, playbackController, song)
|
viewModelScope.playStarredList(libraryRepository, playbackController, song)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun playOnRepeat(song: Song) {
|
|
||||||
viewModelScope.playOnRepeatList(libraryRepository, playbackController, song)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sync() = syncManager.requestSync()
|
fun sync() = syncManager.requestSync()
|
||||||
|
|
||||||
private data class PlayRows(
|
private data class PlayRows(
|
||||||
@@ -119,7 +112,6 @@ class HomeViewModel(
|
|||||||
val recentArtists: List<Artist>,
|
val recentArtists: List<Artist>,
|
||||||
val frequentAlbums: List<Album>,
|
val frequentAlbums: List<Album>,
|
||||||
val frequentArtists: List<Artist>,
|
val frequentArtists: List<Artist>,
|
||||||
val onRepeatSongs: List<AlbumSongItem>,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class Discovery(
|
private data class Discovery(
|
||||||
@@ -142,7 +134,6 @@ internal fun buildHomeFeed(
|
|||||||
addedAlbums: List<Album>,
|
addedAlbums: List<Album>,
|
||||||
rediscover: List<Album>,
|
rediscover: List<Album>,
|
||||||
hasPlayData: Boolean,
|
hasPlayData: Boolean,
|
||||||
onRepeatSongs: List<AlbumSongItem> = emptyList(),
|
|
||||||
): HomeFeed =
|
): HomeFeed =
|
||||||
HomeFeed(
|
HomeFeed(
|
||||||
recentlyPlayedAlbums = recentAlbums.takeIf { hasPlayData }.orEmpty(),
|
recentlyPlayedAlbums = recentAlbums.takeIf { hasPlayData }.orEmpty(),
|
||||||
@@ -152,7 +143,6 @@ internal fun buildHomeFeed(
|
|||||||
genres = genres,
|
genres = genres,
|
||||||
decades = decades,
|
decades = decades,
|
||||||
recentlyStarredSongs = starredSongs,
|
recentlyStarredSongs = starredSongs,
|
||||||
onRepeatSongs = onRepeatSongs,
|
|
||||||
recentlyAddedAlbums = addedAlbums,
|
recentlyAddedAlbums = addedAlbums,
|
||||||
rediscoverAlbums = rediscover,
|
rediscoverAlbums = rediscover,
|
||||||
)
|
)
|
||||||
@@ -183,14 +173,3 @@ internal fun CoroutineScope.playStarredList(
|
|||||||
playbackController.playSongs(song.sourceId, ids, ids.indexOf(song.id).coerceAtLeast(0))
|
playbackController.playSongs(song.sourceId, ids, ids.indexOf(song.id).coerceAtLeast(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun CoroutineScope.playOnRepeatList(
|
|
||||||
libraryRepository: LibraryRepository,
|
|
||||||
playbackController: PlaybackController,
|
|
||||||
song: Song,
|
|
||||||
) {
|
|
||||||
launch {
|
|
||||||
val ids = libraryRepository.onRepeatSongIds(song.sourceId)
|
|
||||||
playbackController.playSongs(song.sourceId, ids, ids.indexOf(song.id).coerceAtLeast(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -335,24 +335,6 @@ class LibraryHomeDaoTest {
|
|||||||
assertEquals(displayed, dao.starredSongIds(sourceId))
|
assertEquals(displayed, dao.starredSongIds(sourceId))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onRepeatKeepsSongsPlayedThisWeekAndOrdersByPlayCount() =
|
|
||||||
runTest {
|
|
||||||
val sourceId = source()
|
|
||||||
val dao = db.libraryDao()
|
|
||||||
dao.upsertSongs(
|
|
||||||
listOf(
|
|
||||||
song(sourceId, "s1", "One", playCount = 3, played = 900),
|
|
||||||
song(sourceId, "s2", "Two", playCount = 9, played = 950),
|
|
||||||
song(sourceId, "s3", "Three", playCount = 100, played = 100),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
val onRepeat = dao.onRepeatSongs(sourceId, cutoff = 500, limit = 5).first()
|
|
||||||
assertEquals(listOf("s2", "s1"), onRepeat.map { it.song.id })
|
|
||||||
assertEquals(listOf("s2", "s1"), dao.onRepeatSongIds(sourceId, cutoff = 500))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun songsByGenrePagingFiltersAndJoinsAlbumCoverArt() =
|
fun songsByGenrePagingFiltersAndJoinsAlbumCoverArt() =
|
||||||
runTest {
|
runTest {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.subtracks.ui.home
|
package com.subtracks.ui.home
|
||||||
|
|
||||||
import com.subtracks.data.model.Album
|
import com.subtracks.data.model.Album
|
||||||
|
import com.subtracks.data.model.Artist
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -49,6 +50,15 @@ class HomeListInfoTest {
|
|||||||
assertEquals("1999", albumListInfo(HomeListRequest(title = "1990s", decade = 1990), album, now))
|
assertEquals("1999", albumListInfo(HomeListRequest(title = "1990s", decade = 1990), album, now))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun artistInfoMatchesTheSection() {
|
||||||
|
val artist = artist(played = secondsAgo(3), playCount = 7)
|
||||||
|
|
||||||
|
assertEquals("3 days ago", artistListInfo(request(HomeSection.RecentlyPlayedArtists), artist, now))
|
||||||
|
assertEquals("7 plays", artistListInfo(request(HomeSection.MostPlayedArtists), artist, now))
|
||||||
|
assertNull(artistListInfo(request(HomeSection.MostPlayedArtists), artist.copy(playCount = 0, played = null), now))
|
||||||
|
}
|
||||||
|
|
||||||
private fun secondsAgo(days: Long): Long = (now - days * 86_400_000L) / 1000L
|
private fun secondsAgo(days: Long): Long = (now - days * 86_400_000L) / 1000L
|
||||||
|
|
||||||
private fun request(section: HomeSection) = HomeListRequest(title = section.title, section = section)
|
private fun request(section: HomeSection) = HomeListRequest(title = section.title, section = section)
|
||||||
@@ -73,4 +83,18 @@ class HomeListInfoTest {
|
|||||||
playCount = playCount,
|
playCount = playCount,
|
||||||
played = played,
|
played = played,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun artist(
|
||||||
|
played: Long?,
|
||||||
|
playCount: Long,
|
||||||
|
) = Artist(
|
||||||
|
sourceId = 1,
|
||||||
|
id = "ar",
|
||||||
|
name = "Artist",
|
||||||
|
albumCount = 1,
|
||||||
|
starred = null,
|
||||||
|
coverArt = null,
|
||||||
|
playCount = playCount,
|
||||||
|
played = played,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,44 @@ class HomeScreenshotTest {
|
|||||||
composeRule.onRoot().captureRoboImage("src/test/screenshots/home_album_list.png")
|
composeRule.onRoot().captureRoboImage("src/test/screenshots/home_album_list.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun homeRecentlyPlayedArtistsList() {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val artists =
|
||||||
|
listOf(
|
||||||
|
listArtist("Artist Yesterday", (now - 86_400_000L) / 1000L),
|
||||||
|
listArtist("Artist This Week", (now - 3 * 86_400_000L) / 1000L),
|
||||||
|
)
|
||||||
|
composeRule.setContent {
|
||||||
|
SubtracksTheme {
|
||||||
|
HomeListScreen(
|
||||||
|
request = HomeListRequest(title = "Recently played artists", section = HomeSection.RecentlyPlayedArtists),
|
||||||
|
albums = remember { flowOf(PagingData.empty<Album>()) }.collectAsLazyPagingItems(),
|
||||||
|
artists = remember { flowOf(PagingData.from(artists)) }.collectAsLazyPagingItems(),
|
||||||
|
songs = remember { flowOf(PagingData.empty<AlbumSongItem>()) }.collectAsLazyPagingItems(),
|
||||||
|
coverArt = { _, _ -> null },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
composeRule.waitUntil(timeoutMillis = 5_000) {
|
||||||
|
composeRule.onAllNodesWithText("Artist Yesterday").fetchSemanticsNodes().isNotEmpty()
|
||||||
|
}
|
||||||
|
composeRule.onRoot().captureRoboImage("src/test/screenshots/home_artist_list.png")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun listArtist(
|
||||||
|
name: String,
|
||||||
|
played: Long,
|
||||||
|
) = Artist(
|
||||||
|
sourceId = 1,
|
||||||
|
id = name,
|
||||||
|
name = name,
|
||||||
|
albumCount = 4,
|
||||||
|
starred = null,
|
||||||
|
coverArt = null,
|
||||||
|
played = played,
|
||||||
|
)
|
||||||
|
|
||||||
private fun listAlbum(
|
private fun listAlbum(
|
||||||
name: String,
|
name: String,
|
||||||
created: Long,
|
created: Long,
|
||||||
@@ -131,7 +169,6 @@ class HomeScreenshotTest {
|
|||||||
genres = listOf("Rock", "Jazz", "Classical", "Electronic", "Pop", "Folk", "Blues"),
|
genres = listOf("Rock", "Jazz", "Classical", "Electronic", "Pop", "Folk", "Blues"),
|
||||||
decades = listOf(1960L, 1970L, 1980L, 1990L, 2000L, 2010L),
|
decades = listOf(1960L, 1970L, 1980L, 1990L, 2000L, 2010L),
|
||||||
recentlyStarredSongs = songs(),
|
recentlyStarredSongs = songs(),
|
||||||
onRepeatSongs = songs(),
|
|
||||||
recentlyAddedAlbums = albums("Added"),
|
recentlyAddedAlbums = albums("Added"),
|
||||||
rediscoverAlbums = albums("Repeat"),
|
rediscoverAlbums = albums("Repeat"),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ class HomeViewModelTest {
|
|||||||
addedAlbums = listOf(album("c")),
|
addedAlbums = listOf(album("c")),
|
||||||
rediscover = listOf(album("d")),
|
rediscover = listOf(album("d")),
|
||||||
hasPlayData = true,
|
hasPlayData = true,
|
||||||
onRepeatSongs = listOf(starred("repeat")),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(listOf("a"), feed.recentlyPlayedAlbums.map { it.id })
|
assertEquals(listOf("a"), feed.recentlyPlayedAlbums.map { it.id })
|
||||||
@@ -36,7 +35,6 @@ class HomeViewModelTest {
|
|||||||
assertEquals(listOf("Rock"), feed.genres)
|
assertEquals(listOf("Rock"), feed.genres)
|
||||||
assertEquals(listOf(1970L), feed.decades)
|
assertEquals(listOf(1970L), feed.decades)
|
||||||
assertEquals(listOf("song"), feed.recentlyStarredSongs.map { it.song.id })
|
assertEquals(listOf("song"), feed.recentlyStarredSongs.map { it.song.id })
|
||||||
assertEquals(listOf("repeat"), feed.onRepeatSongs.map { it.song.id })
|
|
||||||
assertEquals(listOf("c"), feed.recentlyAddedAlbums.map { it.id })
|
assertEquals(listOf("c"), feed.recentlyAddedAlbums.map { it.id })
|
||||||
assertEquals(listOf("d"), feed.rediscoverAlbums.map { it.id })
|
assertEquals(listOf("d"), feed.rediscoverAlbums.map { it.id })
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user