mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
address artist detail review findings
This commit is contained in:
@@ -6,6 +6,7 @@ import com.subtracks.data.model.Playlist
|
||||
import com.subtracks.data.model.PlaylistSong
|
||||
import com.subtracks.data.model.Song
|
||||
import com.subtracks.data.source.MusicSource
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@@ -98,9 +99,13 @@ class SubsonicSource(
|
||||
thumbnail: Boolean = false,
|
||||
): String? =
|
||||
withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
try {
|
||||
SubsonicXml.artistImageUrl(client.get("getArtistInfo2", mapOf("id" to artistId)), thumbnail)
|
||||
}.getOrNull()
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchRanks(): Pair<Map<String, Long>, Map<String, Long>> = fetchRank("frequent") to fetchRank("recent")
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.subtracks.ui.library
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -11,6 +12,7 @@ import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
@@ -94,16 +96,30 @@ fun ArtistDetailScreen(
|
||||
.toDp()
|
||||
}
|
||||
val statusBarTop = with(density) { WindowInsets.statusBars.getTop(this).toDp() }
|
||||
val navBarBottom = with(density) { WindowInsets.navigationBars.getBottom(this).toDp() }
|
||||
val barHeight = statusBarTop + TopAppBarDefaults.TopAppBarExpandedHeight
|
||||
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() }
|
||||
val barFraction by remember {
|
||||
val barFraction by remember(fadeStartPx, fadeEndPx) {
|
||||
derivedStateOf {
|
||||
if (listState.firstVisibleItemIndex > 0) {
|
||||
1f
|
||||
} else {
|
||||
((listState.firstVisibleItemScrollOffset - fadeStartPx) / (fadeEndPx - fadeStartPx)).coerceIn(0f, 1f)
|
||||
when {
|
||||
listState.layoutInfo.visibleItemsInfo.isEmpty() -> {
|
||||
0f
|
||||
}
|
||||
|
||||
!listState.canScrollForward -> {
|
||||
1f
|
||||
}
|
||||
|
||||
listState.firstVisibleItemIndex > 0 -> {
|
||||
1f
|
||||
}
|
||||
|
||||
else -> {
|
||||
((listState.firstVisibleItemScrollOffset - fadeStartPx) / (fadeEndPx - fadeStartPx))
|
||||
.coerceIn(0f, 1f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,11 +134,11 @@ fun ArtistDetailScreen(
|
||||
),
|
||||
)
|
||||
|
||||
Box(modifier.fillMaxSize()) {
|
||||
Box(modifier.fillMaxSize().background(MaterialTheme.colorScheme.background)) {
|
||||
LazyVerticalGrid(
|
||||
state = listState,
|
||||
columns = GridCells.Fixed(2),
|
||||
contentPadding = PaddingValues(bottom = 16.dp),
|
||||
contentPadding = PaddingValues(bottom = 16.dp + navBarBottom),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
@@ -207,7 +223,14 @@ fun ArtistDetailScreen(
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
|
||||
modifier = Modifier.align(Alignment.TopStart).background(Color.Black.copy(alpha = barFraction)),
|
||||
modifier =
|
||||
Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.background(Color.Black.copy(alpha = barFraction))
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
) {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ import com.subtracks.data.model.CoverArtRef
|
||||
import com.subtracks.data.repo.LibraryRepository
|
||||
import com.subtracks.data.repo.SourceRepository
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -33,11 +35,22 @@ class ArtistDetailViewModel(
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val loaded = artist.filterNotNull().first()
|
||||
_art.value = sourceRepository.artistArt(loaded.id) ?: sourceRepository.coverArt(loaded.coverArt)
|
||||
artist
|
||||
.filterNotNull()
|
||||
.distinctUntilChangedBy { it.id }
|
||||
.collectLatest { loaded -> _art.value = resolveArt(loaded) }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun resolveArt(artist: Artist): CoverArtRef? {
|
||||
repeat(3) { attempt ->
|
||||
val ref = sourceRepository.artistArt(artist.id) ?: sourceRepository.coverArt(artist.coverArt)
|
||||
if (ref != null) return ref
|
||||
if (attempt < 2) delay(300)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun coverArt(
|
||||
coverArt: String?,
|
||||
thumbnail: Boolean,
|
||||
|
||||
Reference in New Issue
Block a user