From 37ccfd0b175ffcd8d9b5ed5f528148fa733272c6 Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Sun, 27 Sep 2026 12:54:15 +0000 Subject: [PATCH] confirm the artist delete too Only a single song deletes without asking; the artist's delete now opens the same dialog as the album and playlist, naming the artist and the space it frees. The artist's download action stays in its more-menu rather than getting a visible control, deliberately, so bulk downloading a whole artist is a considered action. --- .../subtracks/ui/library/ArtistDetailScreen.kt | 17 ++++++++++++++++- .../ui/library/ArtistDetailViewModel.kt | 2 ++ .../data/repo/DownloadRepositoryTest.kt | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailScreen.kt b/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailScreen.kt index 0471ee1c..8bd539fa 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailScreen.kt @@ -50,7 +50,9 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -80,6 +82,7 @@ import com.subtracks.data.source.StarType import com.subtracks.playback.PlaybackController import com.subtracks.ui.components.ContextMenuHost import com.subtracks.ui.components.CoverArt +import com.subtracks.ui.components.DeleteDownloadsDialog import com.subtracks.ui.components.ItemActions import com.subtracks.ui.components.MenuTarget import com.subtracks.ui.components.rememberViewportFill @@ -91,6 +94,7 @@ import com.subtracks.ui.theme.baseArtworkColors import com.subtracks.ui.theme.heroBarColor import com.subtracks.ui.theme.rememberArtworkColors import com.subtracks.ui.theme.rememberOverlaidNameBusy +import kotlinx.coroutines.launch import org.koin.compose.koinInject import org.koin.compose.viewmodel.koinViewModel import org.koin.core.parameter.parametersOf @@ -140,6 +144,8 @@ fun ArtistDetailRoute( val art by viewModel.art.collectAsStateWithLifecycle() val artThumbnail by viewModel.artThumbnail.collectAsStateWithLifecycle() val downloadStatus by viewModel.downloadStatus.collectAsStateWithLifecycle() + val scope = rememberCoroutineScope() + var pendingDelete by remember { mutableStateOf(null) } val shortcutArt = remember(coverArtId) { coverArtId?.let { viewModel.coverArt(it, true) } } val actions = ItemActions( @@ -149,7 +155,7 @@ fun ArtistDetailRoute( addToQueue = { playbackController.addToQueue(it.sourceId, it.kind, it.refId) }, downloadArtist = { viewModel.onDownloadAction(BulkDownloadAction.Download) }, cancelArtistDownload = { viewModel.onDownloadAction(BulkDownloadAction.Cancel) }, - deleteArtistDownload = { viewModel.onDownloadAction(BulkDownloadAction.Delete) }, + deleteArtistDownload = { scope.launch { pendingDelete = viewModel.downloadedBytes() } }, setStar = setStar, viewAlbum = onViewAlbum, ) @@ -167,6 +173,15 @@ fun ArtistDetailRoute( starred = artist?.starred != null, onToggleStar = artist?.let { a -> { setStar(StarType.Artist, a.id, a.starred == null) } }, ) + + pendingDelete?.let { bytes -> + DeleteDownloadsDialog( + name = artist?.name.orEmpty(), + bytes = bytes, + onConfirm = { viewModel.onDownloadAction(BulkDownloadAction.Delete) }, + onDismiss = { pendingDelete = null }, + ) + } } @OptIn(ExperimentalMaterial3Api::class) diff --git a/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailViewModel.kt b/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailViewModel.kt index e45557be..e629e832 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailViewModel.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/ArtistDetailViewModel.kt @@ -44,6 +44,8 @@ class ArtistDetailViewModel( .flatMapLatest { downloadRepository.status(it, DownloadList.Artist, artistId) } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), ListDownloadStatus()) + suspend fun downloadedBytes(): Long = downloadRepository.downloadedBytes(sourceId.first(), DownloadList.Artist, artistId) + fun onDownloadAction(action: BulkDownloadAction) { viewModelScope.launch { downloadRepository.applyAction(sourceId.first(), DownloadList.Artist, artistId, action) diff --git a/app/src/test/kotlin/com/subtracks/data/repo/DownloadRepositoryTest.kt b/app/src/test/kotlin/com/subtracks/data/repo/DownloadRepositoryTest.kt index 5e69d467..d8d1e043 100644 --- a/app/src/test/kotlin/com/subtracks/data/repo/DownloadRepositoryTest.kt +++ b/app/src/test/kotlin/com/subtracks/data/repo/DownloadRepositoryTest.kt @@ -667,6 +667,7 @@ class DownloadRepositoryTest { writeFile(1, "s3", "123") assertEquals(5L, repository.downloadedBytes(1, DownloadList.Album, "al1")) + assertEquals(5L, repository.downloadedBytes(1, DownloadList.Artist, "ar1")) } private fun requestedSongIds(): List = engine.requests.map { it.second.path.substringAfterLast('/') }