Improve SearchResultScreen UI

This commit is contained in:
vfsfitvnm
2022-09-24 13:02:52 +02:00
parent 8db6f7a13e
commit e71e34c0d7
6 changed files with 450 additions and 231 deletions

View File

@@ -2,6 +2,7 @@ package it.vfsfitvnm.vimusic.ui.screens.searchresult
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyItemScope
@@ -22,11 +23,10 @@ import it.vfsfitvnm.youtubemusic.YouTube
@ExperimentalAnimationApi
@Composable
inline fun <I : YouTube.Item> ItemSearchResultTab(
inline fun <I : YouTube.Item> ItemSearchResult(
query: String,
filter: String,
crossinline onSearchAgain: () -> Unit,
isArtists: Boolean = false,
viewModel: ItemSearchResultViewModel<I> = viewModel(
key = query + filter,
factory = object : ViewModelProvider.Factory {
@@ -36,7 +36,8 @@ inline fun <I : YouTube.Item> ItemSearchResultTab(
}
}
),
crossinline itemContent: @Composable (LazyItemScope.(I) -> Unit)
crossinline itemContent: @Composable LazyItemScope.(I) -> Unit,
noinline itemShimmer: @Composable BoxScope.() -> Unit,
) {
LazyColumn(
contentPadding = LocalPlayerAwarePaddingValues.current,
@@ -45,7 +46,7 @@ inline fun <I : YouTube.Item> ItemSearchResultTab(
) {
item(
key = "header",
contentType = 0
contentType = 0,
) {
Header(
title = query,
@@ -60,6 +61,7 @@ inline fun <I : YouTube.Item> ItemSearchResultTab(
items(
items = viewModel.items,
key = { it.key!! },
itemContent = itemContent
)
@@ -73,7 +75,8 @@ inline fun <I : YouTube.Item> ItemSearchResultTab(
item {
SearchResultLoadingOrError(
errorMessage = throwable.javaClass.canonicalName,
onRetry = viewModel::fetch
onRetry = viewModel::fetch,
shimmerContent = {}
)
}
} ?: viewModel.continuationResult?.let {
@@ -88,7 +91,7 @@ inline fun <I : YouTube.Item> ItemSearchResultTab(
} ?: item(key = "loading") {
SearchResultLoadingOrError(
itemCount = if (viewModel.items.isEmpty()) 8 else 3,
isLoadingArtists = isArtists
shimmerContent = itemShimmer
)
}
}

View File

@@ -1,7 +1,6 @@
package it.vfsfitvnm.vimusic.ui.screens.searchresult
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
@@ -12,8 +11,11 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class ItemSearchResultViewModel<T : YouTube.Item>(private val query: String, private val filter: String) : ViewModel() {
val items = mutableStateListOf<T>()
class ItemSearchResultViewModel<T : YouTube.Item>(
private val query: String,
private val filter: String
) : ViewModel() {
var items by mutableStateOf(listOf<T>())
var continuationResult by mutableStateOf<Result<String?>?>(null)
@@ -35,7 +37,7 @@ class ItemSearchResultViewModel<T : YouTube.Item>(private val query: String, pri
YouTube.search(query, filter, token)
}?.map { searchResult ->
@Suppress("UNCHECKED_CAST")
items.addAll(searchResult.items as List<T>)
items = items.plus(searchResult.items as List<T>).distinctBy(YouTube.Item::key)
searchResult.continuation
}
}

View File

@@ -1,9 +1,9 @@
package it.vfsfitvnm.vimusic.ui.screens.searchresult
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@@ -21,17 +21,23 @@ import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
import it.vfsfitvnm.vimusic.ui.screens.playlistRoute
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.ui.views.SmallAlbumItem
import it.vfsfitvnm.vimusic.ui.views.SmallArtistItem
import it.vfsfitvnm.vimusic.ui.views.SmallPlaylistItem
import it.vfsfitvnm.vimusic.ui.views.AlbumItem
import it.vfsfitvnm.vimusic.ui.views.AlbumItemShimmer
import it.vfsfitvnm.vimusic.ui.views.ArtistItem
import it.vfsfitvnm.vimusic.ui.views.ArtistItemShimmer
import it.vfsfitvnm.vimusic.ui.views.PlaylistItem
import it.vfsfitvnm.vimusic.ui.views.PlaylistItemShimmer
import it.vfsfitvnm.vimusic.ui.views.SmallSongItem
import it.vfsfitvnm.vimusic.ui.views.SmallVideoItem
import it.vfsfitvnm.vimusic.ui.views.SmallSongItemShimmer
import it.vfsfitvnm.vimusic.ui.views.VideoItem
import it.vfsfitvnm.vimusic.ui.views.VideoItemShimmer
import it.vfsfitvnm.vimusic.utils.asMediaItem
import it.vfsfitvnm.vimusic.utils.forcePlay
import it.vfsfitvnm.vimusic.utils.rememberPreference
import it.vfsfitvnm.vimusic.utils.searchResultScreenTabIndexKey
import it.vfsfitvnm.youtubemusic.YouTube
@ExperimentalFoundationApi
@ExperimentalAnimationApi
@Composable
fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
@@ -76,125 +82,139 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
when (tabIndex) {
0 -> {
val binder = LocalPlayerServiceBinder.current
val thumbnailSizePx = Dimensions.thumbnails.song.px
ItemSearchResultTab<YouTube.Item.Song>(
query = query,
filter = searchFilter,
onSearchAgain = onSearchAgain
) { song ->
SmallSongItem(
song = song,
thumbnailSizePx = thumbnailSizePx,
onClick = {
binder?.stopRadio()
binder?.player?.forcePlay(song.asMediaItem)
binder?.setupRadio(song.info.endpoint)
}
)
}
}
1 -> {
val thumbnailSizeDp = Dimensions.thumbnails.song
val thumbnailSizePx = thumbnailSizeDp.px
ItemSearchResultTab<YouTube.Item.Album>(
query = query,
filter = searchFilter,
onSearchAgain = onSearchAgain
) { album ->
SmallAlbumItem(
album = album,
thumbnailSizePx = thumbnailSizePx,
thumbnailSizeDp = thumbnailSizeDp,
modifier = Modifier
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = { albumRoute(album.info.endpoint?.browseId) }
)
.padding(
vertical = Dimensions.itemsVerticalPadding,
horizontal = 16.dp
)
)
}
}
2 -> {
val thumbnailSizeDp = Dimensions.thumbnails.song
val thumbnailSizePx = thumbnailSizeDp.px
ItemSearchResultTab<YouTube.Item.Artist>(
ItemSearchResult<YouTube.Item.Song>(
query = query,
filter = searchFilter,
onSearchAgain = onSearchAgain,
isArtists = true
) { artist ->
SmallArtistItem(
artist = artist,
thumbnailSizePx = thumbnailSizePx,
thumbnailSizeDp = thumbnailSizeDp,
modifier = Modifier
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = { artistRoute(artist.info.endpoint?.browseId) }
)
.padding(
vertical = Dimensions.itemsVerticalPadding,
horizontal = 16.dp
)
)
}
itemContent = { song ->
SmallSongItem(
song = song,
thumbnailSizePx = thumbnailSizePx,
onClick = {
binder?.stopRadio()
binder?.player?.forcePlay(song.asMediaItem)
binder?.setupRadio(song.info.endpoint)
}
)
},
itemShimmer = {
SmallSongItemShimmer(thumbnailSizeDp = thumbnailSizeDp)
}
)
}
1 -> {
val thumbnailSizeDp = 108.dp
val thumbnailSizePx = thumbnailSizeDp.px
ItemSearchResult<YouTube.Item.Album>(
query = query,
filter = searchFilter,
onSearchAgain = onSearchAgain,
itemContent = { album ->
AlbumItem(
album = album,
thumbnailSizePx = thumbnailSizePx,
thumbnailSizeDp = thumbnailSizeDp,
modifier = Modifier
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = { albumRoute(album.info.endpoint?.browseId) }
)
)
},
itemShimmer = {
AlbumItemShimmer(thumbnailSizeDp = thumbnailSizeDp)
}
)
}
2 -> {
val thumbnailSizeDp = 64.dp
val thumbnailSizePx = thumbnailSizeDp.px
ItemSearchResult<YouTube.Item.Artist>(
query = query,
filter = searchFilter,
onSearchAgain = onSearchAgain,
itemContent = { artist ->
ArtistItem(
artist = artist,
thumbnailSizePx = thumbnailSizePx,
thumbnailSizeDp = thumbnailSizeDp,
modifier = Modifier
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = { artistRoute(artist.info.endpoint?.browseId) }
)
)
},
itemShimmer = {
ArtistItemShimmer(thumbnailSizeDp = thumbnailSizeDp)
}
)
}
3 -> {
val binder = LocalPlayerServiceBinder.current
val thumbnailSizePx = Dimensions.thumbnails.song.px
val thumbnailHeightDp = 72.dp
val thumbnailWidthDp = 128.dp
ItemSearchResultTab<YouTube.Item.Video>(
ItemSearchResult<YouTube.Item.Video>(
query = query,
filter = searchFilter,
onSearchAgain = onSearchAgain
) { video ->
SmallVideoItem(
video = video,
thumbnailSizePx = thumbnailSizePx,
onClick = {
binder?.stopRadio()
binder?.player?.forcePlay(video.asMediaItem)
binder?.setupRadio(video.info.endpoint)
}
)
}
onSearchAgain = onSearchAgain,
itemContent = { video ->
VideoItem(
video = video,
thumbnailWidthDp = thumbnailWidthDp,
thumbnailHeightDp = thumbnailHeightDp,
onClick = {
binder?.stopRadio()
binder?.player?.forcePlay(video.asMediaItem)
binder?.setupRadio(video.info.endpoint)
}
)
},
itemShimmer = {
VideoItemShimmer(
thumbnailHeightDp = thumbnailHeightDp,
thumbnailWidthDp = thumbnailWidthDp
)
}
)
}
4, 5 -> {
val thumbnailSizeDp = Dimensions.thumbnails.song
val thumbnailSizeDp = 108.dp
val thumbnailSizePx = thumbnailSizeDp.px
ItemSearchResultTab<YouTube.Item.Playlist>(
ItemSearchResult<YouTube.Item.Playlist>(
query = query,
filter = searchFilter,
onSearchAgain = onSearchAgain
) { playlist ->
SmallPlaylistItem(
playlist = playlist,
thumbnailSizePx = thumbnailSizePx,
thumbnailSizeDp = thumbnailSizeDp,
modifier = Modifier
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = { playlistRoute(playlist.info.endpoint?.browseId) }
)
.padding(
vertical = Dimensions.itemsVerticalPadding,
horizontal = 16.dp
)
)
}
onSearchAgain = onSearchAgain,
itemContent = { playlist ->
PlaylistItem(
playlist = playlist,
thumbnailSizePx = thumbnailSizePx,
thumbnailSizeDp = thumbnailSizeDp,
modifier = Modifier
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = { playlistRoute(playlist.info.endpoint?.browseId) }
)
)
},
itemShimmer = {
PlaylistItemShimmer(thumbnailSizeDp = thumbnailSizeDp)
}
)
}
}
}