Continue removing Outcome class in favor of Result (search)

This commit is contained in:
vfsfitvnm 2022-07-01 19:32:58 +02:00
parent 21ef7e8d5e
commit 14f46429ef
4 changed files with 111 additions and 135 deletions

View file

@ -69,8 +69,6 @@ fun IntentUriScreen(uri: Uri) {
val density = LocalDensity.current
val binder = LocalPlayerServiceBinder.current
val shimmer = rememberShimmer(shimmerBounds = ShimmerBounds.Window)
var items by remember(uri) {
mutableStateOf<Outcome<List<YouTube.Item.Song>>>(Outcome.Loading)
}
@ -206,7 +204,6 @@ fun IntentUriScreen(uri: Uri) {
}
is Outcome.Loading, is Outcome.Initial -> items(count = 5) { index ->
SmallSongItemShimmer(
shimmer = shimmer,
thumbnailSizeDp = 54.dp,
modifier = Modifier
.alpha(1f - index * 0.175f)

View file

@ -26,22 +26,21 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.valentinilk.shimmer.Shimmer
import com.valentinilk.shimmer.ShimmerBounds
import com.valentinilk.shimmer.rememberShimmer
import com.valentinilk.shimmer.shimmer
import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.ui.components.*
import it.vfsfitvnm.vimusic.ui.components.ChipGroup
import it.vfsfitvnm.vimusic.ui.components.ChipItem
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.ui.views.SongItem
import it.vfsfitvnm.vimusic.utils.*
import it.vfsfitvnm.youtubemusic.Outcome
import it.vfsfitvnm.youtubemusic.YouTube
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@ -61,26 +60,26 @@ fun SearchResultScreen(
val lazyListState = rememberLazyListState()
var continuation by remember(preferences.searchFilter) {
mutableStateOf<Outcome<String?>>(Outcome.Initial)
}
val items = remember(preferences.searchFilter) {
mutableStateListOf<YouTube.Item>()
}
var continuationResult by remember(preferences.searchFilter) {
mutableStateOf<Result<String?>?>(null)
}
val onLoad = relaunchableEffect(preferences.searchFilter) {
withContext(Dispatchers.Main) {
val token = continuation.valueOrNull
val token = continuationResult?.getOrNull()
continuation = Outcome.Loading
continuationResult = null
continuation = withContext(Dispatchers.IO) {
continuationResult = withContext(Dispatchers.IO) {
YouTube.search(query, preferences.searchFilter, token)
}.map { searchResult ->
}?.map { searchResult ->
items.addAll(searchResult.items)
searchResult.continuation
}.recoverWith(token)
}
}
}
@ -116,8 +115,6 @@ fun SearchResultScreen(
}
host {
val shimmer = rememberShimmer(shimmerBounds = ShimmerBounds.Window)
LazyColumn(
state = lazyListState,
horizontalAlignment = Alignment.CenterHorizontally,
@ -237,63 +234,35 @@ fun SearchResultScreen(
)
}
when (val currentResult = continuation) {
is Outcome.Error -> item {
Error(
error = currentResult,
onRetry = onLoad,
modifier = Modifier
.padding(vertical = 16.dp)
)
}
is Outcome.Recovered -> item {
Error(
error = currentResult.error,
onRetry = onLoad,
modifier = Modifier
.padding(vertical = 16.dp)
)
}
is Outcome.Success -> {
if (items.isEmpty()) {
item {
Message(
text = "No results found",
modifier = Modifier
)
}
continuationResult?.getOrNull()?.let {
if (items.isNotEmpty()) {
item {
SideEffect(onLoad)
}
if (currentResult.value != null) {
item {
SideEffect(onLoad)
}
} ?: continuationResult?.exceptionOrNull()?.let { throwable ->
item {
LoadingOrError(
errorMessage = throwable.javaClass.canonicalName,
onRetry = onLoad
)
}
} ?: continuationResult?.let {
if (items.isEmpty()) {
item {
TextCard(
icon = R.drawable.sad
) {
Title(text = "No results found")
Text(text = "Please try a different query or category.")
}
}
}
else -> {}
}
if (continuation is Outcome.Loading || (continuation is Outcome.Success && continuation.valueOrNull != null)) {
items(count = if (items.isEmpty()) 8 else 3, key = { it }) { index ->
when (preferences.searchFilter) {
YouTube.Item.Artist.Filter.value -> SmallArtistItemShimmer(
shimmer = shimmer,
thumbnailSizeDp = 54.dp,
modifier = Modifier
.alpha(1f - index * 0.125f)
.fillMaxWidth()
.padding(vertical = 4.dp, horizontal = 16.dp)
)
else -> SmallSongItemShimmer(
shimmer = shimmer,
thumbnailSizeDp = 54.dp,
modifier = Modifier
.alpha(1f - index * 0.125f)
.fillMaxWidth()
.padding(vertical = 4.dp, horizontal = 16.dp)
)
}
}
} ?: item(key = "loading") {
LoadingOrError(
itemCount = if (items.isEmpty()) 8 else 3,
isLoadingArtists = preferences.searchFilter == YouTube.Item.Artist.Filter.value
)
}
}
}
@ -302,7 +271,6 @@ fun SearchResultScreen(
@Composable
fun SmallSongItemShimmer(
shimmer: Shimmer,
thumbnailSizeDp: Dp,
modifier: Modifier = Modifier
) {
@ -312,7 +280,6 @@ fun SmallSongItemShimmer(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.shimmer(shimmer)
) {
Spacer(
modifier = Modifier
@ -329,7 +296,6 @@ fun SmallSongItemShimmer(
@Composable
fun SmallArtistItemShimmer(
shimmer: Shimmer,
thumbnailSizeDp: Dp,
modifier: Modifier = Modifier
) {
@ -339,7 +305,6 @@ fun SmallArtistItemShimmer(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.shimmer(shimmer)
) {
Spacer(
modifier = Modifier
@ -579,3 +544,37 @@ fun SmallArtistItem(
)
}
}
@Composable
private fun LoadingOrError(
itemCount: Int = 0,
isLoadingArtists: Boolean = false,
errorMessage: String? = null,
onRetry: (() -> Unit)? = null
) {
LoadingOrError(
errorMessage = errorMessage,
onRetry = onRetry,
horizontalAlignment = Alignment.CenterHorizontally
) {
repeat(itemCount) { index ->
if (isLoadingArtists) {
SmallArtistItemShimmer(
thumbnailSizeDp = 54.dp,
modifier = Modifier
.alpha(1f - index * 0.125f)
.fillMaxWidth()
.padding(vertical = 4.dp, horizontal = 16.dp)
)
} else {
SmallSongItemShimmer(
thumbnailSizeDp = 54.dp,
modifier = Modifier
.alpha(1f - index * 0.125f)
.fillMaxWidth()
.padding(vertical = 4.dp, horizontal = 16.dp)
)
}
}
}
}

View file

@ -26,8 +26,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.media3.common.Player
import com.valentinilk.shimmer.ShimmerBounds
import com.valentinilk.shimmer.rememberShimmer
import com.valentinilk.shimmer.shimmer
import it.vfsfitvnm.reordering.rememberReorderingState
import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
@ -132,7 +131,6 @@ fun CurrentPlaylistView(
} else {
MusicBars(
color = LightColorPalette.background,
// shape = RectangleShape,
modifier = Modifier
.height(24.dp)
)
@ -159,12 +157,12 @@ fun CurrentPlaylistView(
item {
if (binder?.isLoadingRadio == true) {
val shimmer = rememberShimmer(shimmerBounds = ShimmerBounds.Window)
Column {
Column(
modifier = Modifier
.shimmer()
) {
repeat(3) { index ->
SmallSongItemShimmer(
shimmer = shimmer,
thumbnailSizeDp = 54.dp,
modifier = Modifier
.alpha(1f - index * 0.125f)

View file

@ -364,43 +364,39 @@ object YouTube {
query: String,
filter: String,
continuation: String?
): Outcome<SearchResult> {
return client.postCatching("/youtubei/v1/search") {
contentType(ContentType.Application.Json)
setBody(
SearchBody(
context = Context.DefaultWeb,
query = query,
params = filter
): Result<SearchResult>? {
return runCatching {
val musicShelfRenderer = client.post("/youtubei/v1/search") {
contentType(ContentType.Application.Json)
setBody(
SearchBody(
context = Context.DefaultWeb,
query = query,
params = filter
)
)
)
parameter("key", Key)
parameter("prettyPrint", false)
parameter("continuation", continuation)
}.flatMap { response ->
if (continuation == null) {
response.bodyCatching<SearchResponse>()
.map { body ->
body
.contents
.tabbedSearchResultsRenderer
.tabs
.firstOrNull()
?.tabRenderer
?.content
?.sectionListRenderer
?.contents
?.lastOrNull()
?.musicShelfRenderer
}
} else {
response.bodyCatching<ContinuationResponse>().map { body ->
body
parameter("key", Key)
parameter("prettyPrint", false)
parameter("continuation", continuation)
}.let { response ->
if (continuation == null) {
response.body<SearchResponse>()
.contents
.tabbedSearchResultsRenderer
.tabs
.firstOrNull()
?.tabRenderer
?.content
?.sectionListRenderer
?.contents
?.lastOrNull()
?.musicShelfRenderer
} else {
response.body<ContinuationResponse>()
.continuationContents
.musicShelfContinuation
}
}
}.map { musicShelfRenderer ->
SearchResult(
items = musicShelfRenderer
?.contents
@ -421,7 +417,7 @@ object YouTube {
?.nextRadioContinuationData
?.continuation
)
}
}.recoverIfCancelled()
}
suspend fun getSearchSuggestions(input: String): Outcome<List<String>?> {
@ -657,7 +653,7 @@ object YouTube {
return if (browseId == null) {
Result.success(null)
} else {
browse2(browseId)?.map { body ->
browse(browseId)?.map { body ->
body.contents
.sectionListRenderer
?.contents
@ -675,21 +671,7 @@ object YouTube {
)
}
suspend fun browse(browseId: String): Outcome<BrowseResponse> {
return client.postCatching("/youtubei/v1/browse") {
contentType(ContentType.Application.Json)
setBody(
BrowseBody(
browseId = browseId,
context = Context.DefaultWeb
)
)
parameter("key", Key)
parameter("prettyPrint", false)
}.bodyCatching()
}
suspend fun browse2(browseId: String): Result<BrowseResponse>? {
suspend fun browse(browseId: String): Result<BrowseResponse>? {
return runCatching<YouTube, BrowseResponse> {
client.post("/youtubei/v1/browse") {
contentType(ContentType.Application.Json)
@ -724,7 +706,7 @@ object YouTube {
}
suspend fun playlistOrAlbum(browseId: String): Result<PlaylistOrAlbum>? {
return browse2(browseId)?.map { body ->
return browse(browseId)?.map { body ->
PlaylistOrAlbum(
title = body
.header
@ -839,7 +821,7 @@ object YouTube {
)
suspend fun artist(browseId: String): Result<Artist>? {
return browse2(browseId)?.map { body ->
return browse(browseId)?.map { body ->
Artist(
name = body
.header