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