close offline detail screens when their downloads are gone

This commit is contained in:
austinried
2026-10-04 00:30:04 +00:00
parent 15e09d8097
commit 669f1aff40
6 changed files with 38 additions and 2 deletions
@@ -12,6 +12,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -65,6 +66,15 @@ fun AlbumDetailRoute(
val discs by viewModel.discs.collectAsStateWithLifecycle(initialValue = emptyList())
val downloads by viewModel.downloads.collectAsStateWithLifecycle()
val downloadStatus by viewModel.downloadStatus.collectAsStateWithLifecycle()
val offline by viewModel.offline.collectAsStateWithLifecycle()
var hadDownloads by remember { mutableStateOf(false) }
LaunchedEffect(downloadStatus.downloaded, offline) {
if (downloadStatus.downloaded > 0) {
hadDownloads = true
} else if (hadDownloads && offline) {
onBack()
}
}
val scope = rememberCoroutineScope()
var pendingDelete by remember { mutableStateOf<Long?>(null) }
val playback by playbackController.state.collectAsStateWithLifecycle()
@@ -45,6 +45,8 @@ class AlbumDetailViewModel(
val downloads: StateFlow<Map<String, SongDownload>> = downloadRepository.states()
val offline: StateFlow<Boolean> = sourceRepository.offline
val downloadStatus: StateFlow<ListDownloadStatus> =
sourceId
.flatMapLatest { downloadRepository.status(it, DownloadList.Album, albumId) }
@@ -47,6 +47,7 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
@@ -148,6 +149,15 @@ fun ArtistDetailRoute(
val art by viewModel.art.collectAsStateWithLifecycle()
val artThumbnail by viewModel.artThumbnail.collectAsStateWithLifecycle()
val downloadStatus by viewModel.downloadStatus.collectAsStateWithLifecycle()
val offline by viewModel.offline.collectAsStateWithLifecycle()
var hadDownloads by remember { mutableStateOf(false) }
LaunchedEffect(downloadStatus.downloaded, offline) {
if (downloadStatus.downloaded > 0) {
hadDownloads = true
} else if (hadDownloads && offline) {
onBack()
}
}
val albumDownloads by viewModel.albumDownloads.collectAsStateWithLifecycle()
val scope = rememberCoroutineScope()
var pendingDelete by remember { mutableStateOf<PendingDownloadDelete?>(null) }
@@ -185,6 +195,7 @@ fun ArtistDetailRoute(
onMore = { artist?.let { contextMenuHost?.show(MenuTarget.Artist(it, artThumbnail ?: art, downloadStatus), actions) } },
starred = artist?.starred != null,
onToggleStar = artist?.let { a -> { setStar(StarType.Artist, a.id, a.starred == null) } },
offline = offline,
)
pendingDelete?.let { pending ->
@@ -214,6 +225,7 @@ fun ArtistDetailScreen(
starred: Boolean = false,
onToggleStar: (() -> Unit)? = null,
onShuffle: () -> Unit = {},
offline: Boolean = false,
modifier: Modifier = Modifier,
) {
val listState = rememberLazyGridState()
@@ -227,6 +239,7 @@ fun ArtistDetailScreen(
val statusBarTop = with(density) { WindowInsets.statusBars.getTop(this).toDp() }
val navBarBottom = with(density) { WindowInsets.navigationBars.getBottom(this).toDp() }
val barHeight = statusBarTop + TopAppBarDefaults.TopAppBarExpandedHeight
val albumCount = if (offline) albums.size.toLong() else artist?.albumCount ?: 0L
val imageTitleTop = ART_HEIGHT - TITLE_INSET - lineHeight
val fadeStartPx = with(density) { (imageTitleTop - barHeight - FADE_LEAD).toPx() }
val fadeEndPx = with(density) { (imageTitleTop + lineHeight / 2 - barHeight).toPx() }
@@ -348,7 +361,7 @@ fun ArtistDetailScreen(
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "${artist?.albumCount ?: 0} ${if (artist?.albumCount == 1L) "album" else "albums"}",
text = "$albumCount ${if (albumCount == 1L) "album" else "albums"}",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.weight(1f),
@@ -38,6 +38,8 @@ class ArtistDetailViewModel(
val artist: Flow<Artist?> = sourceId.flatMapLatest { libraryRepository.artist(it, artistId) }
val offline: StateFlow<Boolean> = sourceRepository.offline
val albums: Flow<List<Album>> =
combine(sourceId, sourceRepository.offline) { id, offline -> id to offline }
.flatMapLatest { (id, offline) -> libraryRepository.artistAlbums(id, artistId, offline) }
@@ -860,7 +860,7 @@ internal fun LibraryTabs(
Icon(
imageVector = Icons.Rounded.CloudOff,
contentDescription = "Offline mode; tap to go online",
tint = MaterialTheme.colorScheme.error,
tint = MaterialTheme.colorScheme.onBackground,
)
}
}
@@ -2,6 +2,7 @@ package com.subtracks.ui.library
import androidx.compose.foundation.combinedClickable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -52,6 +53,14 @@ fun PlaylistDetailRoute(
val downloads by viewModel.downloads.collectAsStateWithLifecycle()
val downloadStatus by viewModel.downloadStatus.collectAsStateWithLifecycle()
val offline by viewModel.offline.collectAsStateWithLifecycle()
var hadDownloads by remember { mutableStateOf(false) }
LaunchedEffect(downloadStatus.downloaded, offline) {
if (downloadStatus.downloaded > 0) {
hadDownloads = true
} else if (hadDownloads && offline) {
onBack()
}
}
val scope = rememberCoroutineScope()
var pendingDelete by remember { mutableStateOf<Long?>(null) }
val playback by playbackController.state.collectAsStateWithLifecycle()