mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
trim the play entries, calm the badge, and only watch the visible tab
The album and playlist menus lose Play and Shuffle and the song menu loses Play; tapping the row or the detail hero buttons already covers those, so the ItemActions play/shuffle callbacks and the artist view-model methods behind them go too. The badge was a filled circle that stood out over the art. It is now a white glyph with a soft shadow in the icon's own shape, with no background behind it. The library screen held all three grouped status flows at once, so the two tabs off screen ran queries for badges nobody could see. It now collects only the visible tab's map.
This commit is contained in:
@@ -1,17 +1,17 @@
|
||||
package com.subtracks.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.DownloadDone
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.blur
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -38,34 +38,44 @@ fun ListDownloadIndicator(
|
||||
val current = status ?: return
|
||||
val kind = current.indicator()
|
||||
if (kind == DownloadIndicator.Hidden) return
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.85f),
|
||||
modifier = modifier.size(24.dp),
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
when (kind) {
|
||||
DownloadIndicator.Complete -> {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.DownloadDone,
|
||||
contentDescription = DOWNLOADED_INDICATOR_TAG,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
}
|
||||
Box(modifier = modifier.size(20.dp), contentAlignment = Alignment.Center) {
|
||||
when (kind) {
|
||||
DownloadIndicator.Complete -> {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.DownloadDone,
|
||||
contentDescription = null,
|
||||
tint = ICON_SHADOW,
|
||||
modifier = Modifier.size(18.dp).blur(3.dp).offset(y = 1.dp),
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.DownloadDone,
|
||||
contentDescription = DOWNLOADED_INDICATOR_TAG,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
}
|
||||
|
||||
DownloadIndicator.InProgress -> {
|
||||
CircularProgressIndicator(
|
||||
progress = { current.downloaded.toFloat() / current.total },
|
||||
strokeWidth = 2.dp,
|
||||
modifier = Modifier.size(14.dp).semantics { contentDescription = DOWNLOADING_INDICATOR_TAG },
|
||||
)
|
||||
}
|
||||
DownloadIndicator.InProgress -> {
|
||||
val progress = { current.downloaded.toFloat() / current.total }
|
||||
CircularProgressIndicator(
|
||||
progress = progress,
|
||||
color = ICON_SHADOW,
|
||||
strokeWidth = 2.dp,
|
||||
modifier = Modifier.size(16.dp).blur(3.dp).offset(y = 1.dp),
|
||||
)
|
||||
CircularProgressIndicator(
|
||||
progress = progress,
|
||||
color = Color.White,
|
||||
strokeWidth = 2.dp,
|
||||
modifier = Modifier.size(16.dp).semantics { contentDescription = DOWNLOADING_INDICATOR_TAG },
|
||||
)
|
||||
}
|
||||
|
||||
DownloadIndicator.Hidden -> {
|
||||
Unit
|
||||
}
|
||||
DownloadIndicator.Hidden -> {
|
||||
Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val ICON_SHADOW = Color.Black.copy(alpha = 0.45f)
|
||||
|
||||
@@ -18,8 +18,6 @@ import androidx.compose.material.icons.rounded.Cancel
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.Download
|
||||
import androidx.compose.material.icons.rounded.Person
|
||||
import androidx.compose.material.icons.rounded.PlayArrow
|
||||
import androidx.compose.material.icons.rounded.Shuffle
|
||||
import androidx.compose.material.icons.rounded.SkipNext
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material.icons.rounded.StarBorder
|
||||
@@ -137,11 +135,6 @@ fun MenuTarget.listDownload(): ListDownloadStatus? =
|
||||
}
|
||||
|
||||
class ItemActions(
|
||||
val playSong: ((SongModel) -> Unit)? = null,
|
||||
val playAlbum: (AlbumModel) -> Unit = {},
|
||||
val shuffleAlbum: (AlbumModel) -> Unit = {},
|
||||
val playPlaylist: (PlaylistModel) -> Unit = {},
|
||||
val shufflePlaylist: (PlaylistModel) -> Unit = {},
|
||||
val playNext: ((QueueRef) -> Unit)? = null,
|
||||
val addToQueue: ((QueueRef) -> Unit)? = null,
|
||||
val download: ((SongModel) -> Unit)? = null,
|
||||
@@ -213,9 +206,6 @@ fun ItemContextMenu(
|
||||
MenuHeader(target)
|
||||
when (target) {
|
||||
is MenuTarget.Song -> {
|
||||
actions.playSong?.let { play ->
|
||||
MenuItem(Icons.Rounded.PlayArrow, "Play") { dismiss { play(target.song) } }
|
||||
}
|
||||
queueItems()
|
||||
DownloadItem(target.download, target.song, actions, ::dismiss)
|
||||
StarItem(
|
||||
@@ -240,8 +230,6 @@ fun ItemContextMenu(
|
||||
}
|
||||
|
||||
is MenuTarget.Album -> {
|
||||
MenuItem(Icons.Rounded.PlayArrow, "Play") { dismiss { actions.playAlbum(target.album) } }
|
||||
MenuItem(Icons.Rounded.Shuffle, "Shuffle") { dismiss { actions.shuffleAlbum(target.album) } }
|
||||
queueItems()
|
||||
BulkDownloadItem(target, actions, ::dismiss)
|
||||
StarItem(
|
||||
@@ -268,8 +256,6 @@ fun ItemContextMenu(
|
||||
}
|
||||
|
||||
is MenuTarget.Playlist -> {
|
||||
MenuItem(Icons.Rounded.PlayArrow, "Play") { dismiss { actions.playPlaylist(target.playlist) } }
|
||||
MenuItem(Icons.Rounded.Shuffle, "Shuffle") { dismiss { actions.shufflePlaylist(target.playlist) } }
|
||||
queueItems()
|
||||
BulkDownloadItem(target, actions, ::dismiss)
|
||||
}
|
||||
|
||||
@@ -79,9 +79,6 @@ fun AlbumDetailRoute(
|
||||
}
|
||||
val actions =
|
||||
ItemActions(
|
||||
playSong = { song -> songs.indexOfFirst { it.id == song.id }.takeIf { it >= 0 }?.let(viewModel::play) },
|
||||
playAlbum = { viewModel.playAll() },
|
||||
shuffleAlbum = { viewModel.shuffle() },
|
||||
playNext = { playbackController.playNext(it.sourceId, it.kind, it.refId) },
|
||||
addToQueue = { playbackController.addToQueue(it.sourceId, it.kind, it.refId) },
|
||||
download = viewModel::download,
|
||||
|
||||
@@ -154,8 +154,6 @@ fun ArtistDetailRoute(
|
||||
val shortcutArt = remember(coverArtId) { coverArtId?.let { viewModel.coverArt(it, true) } }
|
||||
val actions =
|
||||
ItemActions(
|
||||
playAlbum = { viewModel.playAlbum(it.id) },
|
||||
shuffleAlbum = { viewModel.shuffleAlbum(it.id) },
|
||||
playNext = { playbackController.playNext(it.sourceId, it.kind, it.refId) },
|
||||
addToQueue = { playbackController.addToQueue(it.sourceId, it.kind, it.refId) },
|
||||
bulkDownload = { target, action ->
|
||||
|
||||
@@ -99,16 +99,4 @@ class ArtistDetailViewModel(
|
||||
coverArt: String?,
|
||||
thumbnail: Boolean,
|
||||
): CoverArtRef? = sourceRepository.coverArt(coverArt, thumbnail)
|
||||
|
||||
fun playAlbum(albumId: String) {
|
||||
viewModelScope.launch {
|
||||
playbackController.playAlbum(sourceId.first(), albumId, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fun shuffleAlbum(albumId: String) {
|
||||
viewModelScope.launch {
|
||||
playbackController.shuffleAlbum(sourceId.first(), albumId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,17 +216,11 @@ fun LibraryRoute(
|
||||
}
|
||||
val playback by playbackController.state.collectAsStateWithLifecycle()
|
||||
val artwork = rememberArtworkColors(playbackController.coverArt(playback.item, thumbnail = true))
|
||||
val albumDownloads by viewModel.albumDownloads.collectAsStateWithLifecycle()
|
||||
val artistDownloads by viewModel.artistDownloads.collectAsStateWithLifecycle()
|
||||
val playlistDownloads by viewModel.playlistDownloads.collectAsStateWithLifecycle()
|
||||
val downloads by viewModel.downloads(listTab).collectAsStateWithLifecycle()
|
||||
val scope = rememberCoroutineScope()
|
||||
var pendingDelete by remember { mutableStateOf<PendingDownloadDelete?>(null) }
|
||||
val itemActions =
|
||||
ItemActions(
|
||||
playAlbum = { viewModel.playAlbum(it.id) },
|
||||
shuffleAlbum = { viewModel.shuffleAlbum(it.id) },
|
||||
playPlaylist = { viewModel.playPlaylist(it.id) },
|
||||
shufflePlaylist = { viewModel.shufflePlaylist(it.id) },
|
||||
playNext = { playbackController.playNext(it.sourceId, it.kind, it.refId) },
|
||||
addToQueue = { playbackController.addToQueue(it.sourceId, it.kind, it.refId) },
|
||||
bulkDownload = { target, action ->
|
||||
@@ -282,9 +276,9 @@ fun LibraryRoute(
|
||||
},
|
||||
search = search,
|
||||
onSearchChange = { viewModel.setSearch(listTab, it) },
|
||||
albumDownloads = albumDownloads,
|
||||
artistDownloads = artistDownloads,
|
||||
playlistDownloads = playlistDownloads,
|
||||
albumDownloads = downloads.takeIf { listTab == LibraryListTab.Albums }.orEmpty(),
|
||||
artistDownloads = downloads.takeIf { listTab == LibraryListTab.Artists }.orEmpty(),
|
||||
playlistDownloads = downloads.takeIf { listTab == LibraryListTab.Playlists }.orEmpty(),
|
||||
)
|
||||
|
||||
pendingDelete?.let { pending ->
|
||||
|
||||
@@ -95,21 +95,28 @@ class LibraryViewModel(
|
||||
|
||||
private val activeSourceId = libraryRepository.activeSourceId.filterNotNull()
|
||||
|
||||
val albumDownloads: StateFlow<Map<String, ListDownloadStatus>> =
|
||||
private val albumDownloads: StateFlow<Map<String, ListDownloadStatus>> =
|
||||
activeSourceId
|
||||
.flatMapLatest { downloadRepository.statuses(it, DownloadList.Album) }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyMap())
|
||||
|
||||
val artistDownloads: StateFlow<Map<String, ListDownloadStatus>> =
|
||||
private val artistDownloads: StateFlow<Map<String, ListDownloadStatus>> =
|
||||
activeSourceId
|
||||
.flatMapLatest { downloadRepository.statuses(it, DownloadList.Artist) }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyMap())
|
||||
|
||||
val playlistDownloads: StateFlow<Map<String, ListDownloadStatus>> =
|
||||
private val playlistDownloads: StateFlow<Map<String, ListDownloadStatus>> =
|
||||
activeSourceId
|
||||
.flatMapLatest { downloadRepository.statuses(it, DownloadList.Playlist) }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyMap())
|
||||
|
||||
fun downloads(tab: LibraryListTab): StateFlow<Map<String, ListDownloadStatus>> =
|
||||
when (tab) {
|
||||
LibraryListTab.Albums -> albumDownloads
|
||||
LibraryListTab.Artists -> artistDownloads
|
||||
LibraryListTab.Playlists -> playlistDownloads
|
||||
}
|
||||
|
||||
suspend fun downloadedBytes(
|
||||
list: DownloadList,
|
||||
refId: String,
|
||||
@@ -149,30 +156,6 @@ class LibraryViewModel(
|
||||
thumbnail: Boolean,
|
||||
): CoverArtRef? = sourceRepository.coverArt(coverArt, thumbnail)
|
||||
|
||||
fun playAlbum(albumId: String) {
|
||||
viewModelScope.launch {
|
||||
sourceRepository.activeSourceIdOnce()?.let { playbackController.playAlbum(it, albumId, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
fun shuffleAlbum(albumId: String) {
|
||||
viewModelScope.launch {
|
||||
sourceRepository.activeSourceIdOnce()?.let { playbackController.shuffleAlbum(it, albumId) }
|
||||
}
|
||||
}
|
||||
|
||||
fun playPlaylist(playlistId: String) {
|
||||
viewModelScope.launch {
|
||||
sourceRepository.activeSourceIdOnce()?.let { playbackController.playPlaylist(it, playlistId, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
fun shufflePlaylist(playlistId: String) {
|
||||
viewModelScope.launch {
|
||||
sourceRepository.activeSourceIdOnce()?.let { playbackController.shufflePlaylist(it, playlistId) }
|
||||
}
|
||||
}
|
||||
|
||||
fun sync() = syncManager.requestSync()
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +64,6 @@ fun PlaylistDetailRoute(
|
||||
}
|
||||
val actions =
|
||||
ItemActions(
|
||||
playPlaylist = { viewModel.playAll() },
|
||||
shufflePlaylist = { viewModel.shuffle() },
|
||||
playNext = { playbackController.playNext(it.sourceId, it.kind, it.refId) },
|
||||
addToQueue = { playbackController.addToQueue(it.sourceId, it.kind, it.refId) },
|
||||
download = viewModel::download,
|
||||
|
||||
Reference in New Issue
Block a user