Continue removing Outcome class in favor of Result (search)
This commit is contained in:
parent
21ef7e8d5e
commit
14f46429ef
4 changed files with 111 additions and 135 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue