Hide floating action button when scrolling down and add scroll to top button to each screen
This commit is contained in:
@@ -0,0 +1,149 @@
|
|||||||
|
package it.vfsfitvnm.vimusic.ui.components.themed
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.animation.core.MutableTransitionState
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.core.updateTransition
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.ScrollState
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues
|
||||||
|
import it.vfsfitvnm.vimusic.R
|
||||||
|
import it.vfsfitvnm.vimusic.utils.isScrollingDown
|
||||||
|
import it.vfsfitvnm.vimusic.utils.isScrollingDownToIsFar
|
||||||
|
import it.vfsfitvnm.vimusic.utils.smoothScrollToTop
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@Composable
|
||||||
|
fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
||||||
|
lazyGridState: LazyGridState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
iconId: Int? = null,
|
||||||
|
onClick: (() -> Unit)? = null,
|
||||||
|
) {
|
||||||
|
val transitionState = remember {
|
||||||
|
MutableTransitionState(false to false)
|
||||||
|
}.apply { targetState = lazyGridState.isScrollingDownToIsFar() }
|
||||||
|
|
||||||
|
FloatingActions(
|
||||||
|
transitionState = transitionState,
|
||||||
|
onScrollToTop = lazyGridState::smoothScrollToTop,
|
||||||
|
iconId = iconId,
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@Composable
|
||||||
|
fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
||||||
|
lazyListState: LazyListState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
iconId: Int? = null,
|
||||||
|
onClick: (() -> Unit)? = null,
|
||||||
|
) {
|
||||||
|
val transitionState = remember {
|
||||||
|
MutableTransitionState(false to false)
|
||||||
|
}.apply { targetState = lazyListState.isScrollingDownToIsFar() }
|
||||||
|
|
||||||
|
FloatingActions(
|
||||||
|
transitionState = transitionState,
|
||||||
|
onScrollToTop = lazyListState::smoothScrollToTop,
|
||||||
|
iconId = iconId,
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@Composable
|
||||||
|
fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
||||||
|
scrollState: ScrollState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
iconId: Int? = null,
|
||||||
|
onClick: (() -> Unit)? = null,
|
||||||
|
) {
|
||||||
|
val transitionState = remember {
|
||||||
|
MutableTransitionState(false to false)
|
||||||
|
}.apply { targetState = scrollState.isScrollingDown() to false }
|
||||||
|
|
||||||
|
FloatingActions(
|
||||||
|
transitionState = transitionState,
|
||||||
|
iconId = iconId,
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@Composable
|
||||||
|
fun BoxScope.FloatingActions(
|
||||||
|
transitionState: MutableTransitionState<Pair<Boolean, Boolean>>,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onScrollToTop: (suspend () -> Unit)? = null,
|
||||||
|
iconId: Int? = null,
|
||||||
|
onClick: (() -> Unit)? = null,
|
||||||
|
) {
|
||||||
|
val transition = updateTransition(transitionState, "FloatingActionsContainer")
|
||||||
|
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
verticalAlignment = Alignment.Bottom,
|
||||||
|
modifier = modifier
|
||||||
|
.align(Alignment.BottomEnd)
|
||||||
|
.padding(end = 16.dp)
|
||||||
|
.padding(LocalPlayerAwarePaddingValues.current)
|
||||||
|
) {
|
||||||
|
onScrollToTop?.let {
|
||||||
|
transition.AnimatedVisibility(
|
||||||
|
visible = { it.first && it.second },
|
||||||
|
enter = slideInVertically(tween(500, if (iconId == null) 0 else 100)) { it },
|
||||||
|
exit = slideOutVertically(tween(500, 0)) { it },
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
SecondaryButton(
|
||||||
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
onScrollToTop()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
iconId = R.drawable.chevron_up,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(bottom = 16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iconId?.let {
|
||||||
|
onClick?.let {
|
||||||
|
transition.AnimatedVisibility(
|
||||||
|
visible = { it.first },
|
||||||
|
enter = slideInVertically(tween(500, 0)) { it },
|
||||||
|
exit = slideOutVertically(tween(500, 100)) { it },
|
||||||
|
) {
|
||||||
|
PrimaryButton(
|
||||||
|
iconId = iconId,
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(bottom = 16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,11 +35,11 @@ import it.vfsfitvnm.vimusic.utils.isLandscape
|
|||||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NavigationRail(
|
inline fun NavigationRail(
|
||||||
topIconButtonId: Int,
|
topIconButtonId: Int,
|
||||||
onTopIconButtonClick: () -> Unit,
|
noinline onTopIconButtonClick: () -> Unit,
|
||||||
tabIndex: Int,
|
tabIndex: Int,
|
||||||
onTabIndexChanged: (Int) -> Unit,
|
crossinline onTabIndexChanged: (Int) -> Unit,
|
||||||
content: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit,
|
content: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
@@ -144,7 +144,7 @@ fun NavigationRail(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Modifier.vertical(enabled: Boolean = true) =
|
fun Modifier.vertical(enabled: Boolean = true) =
|
||||||
if (enabled)
|
if (enabled)
|
||||||
layout { measurable, constraints ->
|
layout { measurable, constraints ->
|
||||||
val placeable = measurable.measure(constraints)
|
val placeable = measurable.measure(constraints)
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import androidx.compose.foundation.Image
|
|||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -16,11 +14,10 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues
|
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BoxScope.PrimaryButton(
|
fun PrimaryButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
@DrawableRes iconId: Int,
|
@DrawableRes iconId: Int,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@@ -30,9 +27,6 @@ fun BoxScope.PrimaryButton(
|
|||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.align(Alignment.BottomEnd)
|
|
||||||
.padding(all = 16.dp)
|
|
||||||
.padding(LocalPlayerAwarePaddingValues.current)
|
|
||||||
.clip(RoundedCornerShape(16.dp))
|
.clip(RoundedCornerShape(16.dp))
|
||||||
.clickable(enabled = isEnabled, onClick = onClick)
|
.clickable(enabled = isEnabled, onClick = onClick)
|
||||||
.background(colorPalette.background2)
|
.background(colorPalette.background2)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package it.vfsfitvnm.vimusic.ui.components.themed
|
package it.vfsfitvnm.vimusic.ui.components.themed
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.AnimatedContentScope
|
import androidx.compose.animation.AnimatedContentScope
|
||||||
import androidx.compose.animation.AnimatedVisibilityScope
|
import androidx.compose.animation.AnimatedVisibilityScope
|
||||||
@@ -19,7 +18,6 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
|
|
||||||
@SuppressLint("ModifierParameter")
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun Scaffold(
|
fun Scaffold(
|
||||||
@@ -28,8 +26,6 @@ fun Scaffold(
|
|||||||
tabIndex: Int,
|
tabIndex: Int,
|
||||||
onTabChanged: (Int) -> Unit,
|
onTabChanged: (Int) -> Unit,
|
||||||
tabColumnContent: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit,
|
tabColumnContent: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit,
|
||||||
primaryIconButtonId: Int? = null,
|
|
||||||
onPrimaryIconButtonClick: () -> Unit = {},
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
content: @Composable AnimatedVisibilityScope.(Int) -> Unit
|
content: @Composable AnimatedVisibilityScope.(Int) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -69,14 +65,7 @@ fun Scaffold(
|
|||||||
slideIntoContainer(slideDirection, animationSpec) with
|
slideIntoContainer(slideDirection, animationSpec) with
|
||||||
slideOutOfContainer(slideDirection, animationSpec)
|
slideOutOfContainer(slideDirection, animationSpec)
|
||||||
},
|
},
|
||||||
content = content,
|
content = content
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
primaryIconButtonId?.let {
|
|
||||||
PrimaryButton(
|
|
||||||
iconId = primaryIconButtonId,
|
|
||||||
onClick = onPrimaryIconButtonClick
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
package it.vfsfitvnm.vimusic.ui.components.themed
|
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.slideInVertically
|
|
||||||
import androidx.compose.animation.slideOutVertically
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.rotate
|
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues
|
|
||||||
import it.vfsfitvnm.vimusic.R
|
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
|
||||||
import it.vfsfitvnm.vimusic.utils.smoothScrollToTop
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ScrollToTop(
|
|
||||||
lazyListState: LazyListState,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val showScrollTopButton by remember {
|
|
||||||
derivedStateOf {
|
|
||||||
lazyListState.firstVisibleItemIndex > lazyListState.layoutInfo.visibleItemsInfo.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollToTop(
|
|
||||||
isVisible = showScrollTopButton,
|
|
||||||
onClick = lazyListState::smoothScrollToTop,
|
|
||||||
modifier = modifier
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ScrollToTop(
|
|
||||||
isVisible: Boolean,
|
|
||||||
onClick: suspend () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = isVisible,
|
|
||||||
enter = slideInVertically { it },
|
|
||||||
exit = slideOutVertically { it },
|
|
||||||
modifier = modifier
|
|
||||||
) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(all = 16.dp)
|
|
||||||
.padding(LocalPlayerAwarePaddingValues.current)
|
|
||||||
.clickable {
|
|
||||||
coroutineScope.launch {
|
|
||||||
onClick()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.size(32.dp)
|
|
||||||
) {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(R.drawable.chevron_down),
|
|
||||||
contentDescription = null,
|
|
||||||
colorFilter = ColorFilter.tint(LocalAppearance.current.colorPalette.text),
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.Center)
|
|
||||||
.rotate(180f)
|
|
||||||
.size(20.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package it.vfsfitvnm.vimusic.ui.components.themed
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SecondaryButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
@DrawableRes iconId: Int,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
isEnabled: Boolean = true,
|
||||||
|
) {
|
||||||
|
val (colorPalette) = LocalAppearance.current
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.clip(CircleShape)
|
||||||
|
.clickable(enabled = isEnabled, onClick = onClick)
|
||||||
|
.background(colorPalette.background2)
|
||||||
|
.size(48.dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(iconId),
|
||||||
|
contentDescription = null,
|
||||||
|
colorFilter = ColorFilter.tint(colorPalette.text),
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.Center)
|
||||||
|
.size(18.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.text.BasicText
|
import androidx.compose.foundation.text.BasicText
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -24,9 +25,9 @@ import it.vfsfitvnm.vimusic.models.DetailedSong
|
|||||||
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.LayoutWithAdaptiveThumbnail
|
import it.vfsfitvnm.vimusic.ui.components.themed.LayoutWithAdaptiveThumbnail
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.PrimaryButton
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
||||||
import it.vfsfitvnm.vimusic.ui.items.SongItem
|
import it.vfsfitvnm.vimusic.ui.items.SongItem
|
||||||
import it.vfsfitvnm.vimusic.ui.items.SongItemPlaceholder
|
import it.vfsfitvnm.vimusic.ui.items.SongItemPlaceholder
|
||||||
@@ -68,9 +69,12 @@ fun AlbumSongs(
|
|||||||
|
|
||||||
val thumbnailSizeDp = Dimensions.thumbnails.song
|
val thumbnailSizeDp = Dimensions.thumbnails.song
|
||||||
|
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
||||||
LayoutWithAdaptiveThumbnail(thumbnailContent = thumbnailContent) {
|
LayoutWithAdaptiveThumbnail(thumbnailContent = thumbnailContent) {
|
||||||
Box {
|
Box {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
state = lazyListState,
|
||||||
contentPadding = LocalPlayerAwarePaddingValues.current,
|
contentPadding = LocalPlayerAwarePaddingValues.current,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(colorPalette.background0)
|
.background(colorPalette.background0)
|
||||||
@@ -152,15 +156,17 @@ fun AlbumSongs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimaryButton(
|
FloatingActionsContainerWithScrollToTop(
|
||||||
|
lazyListState = lazyListState,
|
||||||
iconId = R.drawable.shuffle,
|
iconId = R.drawable.shuffle,
|
||||||
isEnabled = songs.isNotEmpty(),
|
|
||||||
onClick = {
|
onClick = {
|
||||||
|
if (songs.isNotEmpty()) {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(
|
binder?.player?.forcePlayFromBeginning(
|
||||||
songs.shuffled().map(DetailedSong::asMediaItem)
|
songs.shuffled().map(DetailedSong::asMediaItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -21,9 +22,9 @@ import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
|||||||
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.LayoutWithAdaptiveThumbnail
|
import it.vfsfitvnm.vimusic.ui.components.themed.LayoutWithAdaptiveThumbnail
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.PrimaryButton
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
||||||
import it.vfsfitvnm.vimusic.ui.items.SongItem
|
import it.vfsfitvnm.vimusic.ui.items.SongItem
|
||||||
import it.vfsfitvnm.vimusic.ui.items.SongItemPlaceholder
|
import it.vfsfitvnm.vimusic.ui.items.SongItemPlaceholder
|
||||||
@@ -63,9 +64,12 @@ fun ArtistLocalSongs(
|
|||||||
val songThumbnailSizeDp = Dimensions.thumbnails.song
|
val songThumbnailSizeDp = Dimensions.thumbnails.song
|
||||||
val songThumbnailSizePx = songThumbnailSizeDp.px
|
val songThumbnailSizePx = songThumbnailSizeDp.px
|
||||||
|
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
||||||
LayoutWithAdaptiveThumbnail(thumbnailContent = thumbnailContent) {
|
LayoutWithAdaptiveThumbnail(thumbnailContent = thumbnailContent) {
|
||||||
Box {
|
Box {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
state = lazyListState,
|
||||||
contentPadding = LocalPlayerAwarePaddingValues.current,
|
contentPadding = LocalPlayerAwarePaddingValues.current,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(colorPalette.background0)
|
.background(colorPalette.background0)
|
||||||
@@ -128,15 +132,19 @@ fun ArtistLocalSongs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimaryButton(
|
FloatingActionsContainerWithScrollToTop(
|
||||||
|
lazyListState = lazyListState,
|
||||||
iconId = R.drawable.shuffle,
|
iconId = R.drawable.shuffle,
|
||||||
isEnabled = !songs.isNullOrEmpty(),
|
|
||||||
onClick = {
|
onClick = {
|
||||||
|
songs?.let { songs ->
|
||||||
|
if (songs.isNotEmpty()) {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(
|
binder?.player?.forcePlayFromBeginning(
|
||||||
songs!!.shuffled().map(DetailedSong::asMediaItem)
|
songs.shuffled().map(DetailedSong::asMediaItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
|||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.LayoutWithAdaptiveThumbnail
|
import it.vfsfitvnm.vimusic.ui.components.themed.LayoutWithAdaptiveThumbnail
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.PrimaryButton
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
||||||
import it.vfsfitvnm.vimusic.ui.items.AlbumItem
|
import it.vfsfitvnm.vimusic.ui.items.AlbumItem
|
||||||
@@ -70,6 +70,8 @@ fun ArtistOverview(
|
|||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
.padding(top = 24.dp, bottom = 8.dp)
|
.padding(top = 24.dp, bottom = 8.dp)
|
||||||
|
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
LayoutWithAdaptiveThumbnail(thumbnailContent = thumbnailContent) {
|
LayoutWithAdaptiveThumbnail(thumbnailContent = thumbnailContent) {
|
||||||
Box {
|
Box {
|
||||||
Column(
|
Column(
|
||||||
@@ -77,7 +79,7 @@ fun ArtistOverview(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(colorPalette.background0)
|
.background(colorPalette.background0)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(scrollState)
|
||||||
.padding(LocalPlayerAwarePaddingValues.current)
|
.padding(LocalPlayerAwarePaddingValues.current)
|
||||||
) {
|
) {
|
||||||
headerContent {
|
headerContent {
|
||||||
@@ -258,7 +260,8 @@ fun ArtistOverview(
|
|||||||
}
|
}
|
||||||
|
|
||||||
youtubeArtistPage?.shuffleEndpoint?.let { shuffleEndpoint ->
|
youtubeArtistPage?.shuffleEndpoint?.let { shuffleEndpoint ->
|
||||||
PrimaryButton(
|
FloatingActionsContainerWithScrollToTop(
|
||||||
|
scrollState = scrollState,
|
||||||
iconId = R.drawable.shuffle,
|
iconId = R.drawable.shuffle,
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -20,6 +21,7 @@ import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
|
|||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.InFavoritesMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.InFavoritesMediaItemMenu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
|
||||||
@@ -70,8 +72,11 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
|||||||
val thumbnailSizeDp = Dimensions.thumbnails.song
|
val thumbnailSizeDp = Dimensions.thumbnails.song
|
||||||
val thumbnailSize = thumbnailSizeDp.px
|
val thumbnailSize = thumbnailSizeDp.px
|
||||||
|
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
||||||
Box {
|
Box {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
state = lazyListState,
|
||||||
contentPadding = LocalPlayerAwarePaddingValues.current,
|
contentPadding = LocalPlayerAwarePaddingValues.current,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(colorPalette.background0)
|
.background(colorPalette.background0)
|
||||||
@@ -120,6 +125,7 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
|||||||
song = song,
|
song = song,
|
||||||
onDismiss = menuState::hide
|
onDismiss = menuState::hide
|
||||||
)
|
)
|
||||||
|
|
||||||
BuiltInPlaylist.Offline -> InHistoryMediaItemMenu(
|
BuiltInPlaylist.Offline -> InHistoryMediaItemMenu(
|
||||||
song = song,
|
song = song,
|
||||||
onDismiss = menuState::hide
|
onDismiss = menuState::hide
|
||||||
@@ -129,7 +135,10 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
|||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayAtIndex(songs.map(DetailedSong::asMediaItem), index)
|
binder?.player?.forcePlayAtIndex(
|
||||||
|
songs.map(DetailedSong::asMediaItem),
|
||||||
|
index
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.animateItemPlacement()
|
.animateItemPlacement()
|
||||||
@@ -137,15 +146,17 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimaryButton(
|
FloatingActionsContainerWithScrollToTop(
|
||||||
|
lazyListState = lazyListState,
|
||||||
iconId = R.drawable.shuffle,
|
iconId = R.drawable.shuffle,
|
||||||
isEnabled = songs.isNotEmpty(),
|
|
||||||
onClick = {
|
onClick = {
|
||||||
|
if (songs.isNotEmpty()) {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(
|
binder?.player?.forcePlayFromBeginning(
|
||||||
songs.shuffled().map(DetailedSong::asMediaItem)
|
songs.shuffled().map(DetailedSong::asMediaItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ import androidx.compose.animation.core.tween
|
|||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -25,6 +27,7 @@ import it.vfsfitvnm.vimusic.enums.AlbumSortBy
|
|||||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||||
import it.vfsfitvnm.vimusic.models.Album
|
import it.vfsfitvnm.vimusic.models.Album
|
||||||
import it.vfsfitvnm.vimusic.savers.AlbumListSaver
|
import it.vfsfitvnm.vimusic.savers.AlbumListSaver
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
import it.vfsfitvnm.vimusic.ui.items.AlbumItem
|
import it.vfsfitvnm.vimusic.ui.items.AlbumItem
|
||||||
@@ -42,7 +45,8 @@ import kotlinx.coroutines.flow.flowOn
|
|||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeAlbums(
|
fun HomeAlbums(
|
||||||
onAlbumClick: (Album) -> Unit
|
onAlbumClick: (Album) -> Unit,
|
||||||
|
onSearchClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val (colorPalette) = LocalAppearance.current
|
val (colorPalette) = LocalAppearance.current
|
||||||
|
|
||||||
@@ -68,7 +72,11 @@ fun HomeAlbums(
|
|||||||
animationSpec = tween(durationMillis = 400, easing = LinearEasing)
|
animationSpec = tween(durationMillis = 400, easing = LinearEasing)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
||||||
|
Box {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
state = lazyListState,
|
||||||
contentPadding = LocalPlayerAwarePaddingValues.current,
|
contentPadding = LocalPlayerAwarePaddingValues.current,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(colorPalette.background0)
|
.background(colorPalette.background0)
|
||||||
@@ -126,4 +134,11 @@ fun HomeAlbums(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FloatingActionsContainerWithScrollToTop(
|
||||||
|
lazyListState = lazyListState,
|
||||||
|
iconId = R.drawable.search,
|
||||||
|
onClick = onSearchClick
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi
|
|||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
@@ -15,6 +16,7 @@ import androidx.compose.foundation.lazy.grid.GridCells
|
|||||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -29,6 +31,7 @@ import it.vfsfitvnm.vimusic.enums.ArtistSortBy
|
|||||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||||
import it.vfsfitvnm.vimusic.models.Artist
|
import it.vfsfitvnm.vimusic.models.Artist
|
||||||
import it.vfsfitvnm.vimusic.savers.ArtistListSaver
|
import it.vfsfitvnm.vimusic.savers.ArtistListSaver
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
import it.vfsfitvnm.vimusic.ui.items.ArtistItem
|
import it.vfsfitvnm.vimusic.ui.items.ArtistItem
|
||||||
@@ -46,7 +49,8 @@ import kotlinx.coroutines.flow.flowOn
|
|||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeArtistList(
|
fun HomeArtistList(
|
||||||
onArtistClick: (Artist) -> Unit
|
onArtistClick: (Artist) -> Unit,
|
||||||
|
onSearchClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val (colorPalette) = LocalAppearance.current
|
val (colorPalette) = LocalAppearance.current
|
||||||
|
|
||||||
@@ -72,7 +76,11 @@ fun HomeArtistList(
|
|||||||
animationSpec = tween(durationMillis = 400, easing = LinearEasing)
|
animationSpec = tween(durationMillis = 400, easing = LinearEasing)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val lazyGridState = rememberLazyGridState()
|
||||||
|
|
||||||
|
Box {
|
||||||
LazyVerticalGrid(
|
LazyVerticalGrid(
|
||||||
|
state = lazyGridState,
|
||||||
columns = GridCells.Adaptive(Dimensions.thumbnails.song * 2 + Dimensions.itemsVerticalPadding * 2),
|
columns = GridCells.Adaptive(Dimensions.thumbnails.song * 2 + Dimensions.itemsVerticalPadding * 2),
|
||||||
contentPadding = LocalPlayerAwarePaddingValues.current,
|
contentPadding = LocalPlayerAwarePaddingValues.current,
|
||||||
verticalArrangement = Arrangement.spacedBy(Dimensions.itemsVerticalPadding * 2),
|
verticalArrangement = Arrangement.spacedBy(Dimensions.itemsVerticalPadding * 2),
|
||||||
@@ -129,4 +137,11 @@ fun HomeArtistList(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FloatingActionsContainerWithScrollToTop(
|
||||||
|
lazyGridState = lazyGridState,
|
||||||
|
iconId = R.drawable.search,
|
||||||
|
onClick = onSearchClick
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package it.vfsfitvnm.vimusic.ui.screens.home
|
package it.vfsfitvnm.vimusic.ui.screens.home
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.core.LinearEasing
|
import androidx.compose.animation.core.LinearEasing
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
@@ -7,6 +8,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi
|
|||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
@@ -14,6 +16,7 @@ import androidx.compose.foundation.lazy.grid.GridCells
|
|||||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -32,6 +35,7 @@ import it.vfsfitvnm.vimusic.enums.SortOrder
|
|||||||
import it.vfsfitvnm.vimusic.models.Playlist
|
import it.vfsfitvnm.vimusic.models.Playlist
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.savers.PlaylistPreviewListSaver
|
import it.vfsfitvnm.vimusic.savers.PlaylistPreviewListSaver
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
||||||
@@ -47,11 +51,13 @@ import it.vfsfitvnm.vimusic.utils.rememberPreference
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun HomePlaylists(
|
fun HomePlaylists(
|
||||||
onBuiltInPlaylist: (BuiltInPlaylist) -> Unit,
|
onBuiltInPlaylist: (BuiltInPlaylist) -> Unit,
|
||||||
onPlaylistClick: (Playlist) -> Unit,
|
onPlaylistClick: (Playlist) -> Unit,
|
||||||
|
onSearchClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val (colorPalette) = LocalAppearance.current
|
val (colorPalette) = LocalAppearance.current
|
||||||
|
|
||||||
@@ -95,7 +101,11 @@ fun HomePlaylists(
|
|||||||
val thumbnailSizeDp = 108.dp
|
val thumbnailSizeDp = 108.dp
|
||||||
val thumbnailSizePx = thumbnailSizeDp.px
|
val thumbnailSizePx = thumbnailSizeDp.px
|
||||||
|
|
||||||
|
val lazyGridState = rememberLazyGridState()
|
||||||
|
|
||||||
|
Box {
|
||||||
LazyVerticalGrid(
|
LazyVerticalGrid(
|
||||||
|
state = lazyGridState,
|
||||||
columns = GridCells.Adaptive(Dimensions.thumbnails.song * 2 + Dimensions.itemsVerticalPadding * 2),
|
columns = GridCells.Adaptive(Dimensions.thumbnails.song * 2 + Dimensions.itemsVerticalPadding * 2),
|
||||||
contentPadding = LocalPlayerAwarePaddingValues.current,
|
contentPadding = LocalPlayerAwarePaddingValues.current,
|
||||||
verticalArrangement = Arrangement.spacedBy(Dimensions.itemsVerticalPadding * 2),
|
verticalArrangement = Arrangement.spacedBy(Dimensions.itemsVerticalPadding * 2),
|
||||||
@@ -192,4 +202,11 @@ fun HomePlaylists(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FloatingActionsContainerWithScrollToTop(
|
||||||
|
lazyGridState = lazyGridState,
|
||||||
|
iconId = R.drawable.search,
|
||||||
|
onClick = onSearchClick
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,8 +114,6 @@ fun HomeScreen(onPlaylistUrl: (String) -> Unit) {
|
|||||||
Item(3, "Artists", R.drawable.person)
|
Item(3, "Artists", R.drawable.person)
|
||||||
Item(4, "Albums", R.drawable.disc)
|
Item(4, "Albums", R.drawable.disc)
|
||||||
},
|
},
|
||||||
primaryIconButtonId = R.drawable.search,
|
|
||||||
onPrimaryIconButtonClick = { searchRoute("") }
|
|
||||||
) { currentTabIndex ->
|
) { currentTabIndex ->
|
||||||
saveableStateHolder.SaveableStateProvider(key = currentTabIndex) {
|
saveableStateHolder.SaveableStateProvider(key = currentTabIndex) {
|
||||||
when (currentTabIndex) {
|
when (currentTabIndex) {
|
||||||
@@ -123,14 +121,24 @@ fun HomeScreen(onPlaylistUrl: (String) -> Unit) {
|
|||||||
onAlbumClick = { albumRoute(it) },
|
onAlbumClick = { albumRoute(it) },
|
||||||
onArtistClick = { artistRoute(it) },
|
onArtistClick = { artistRoute(it) },
|
||||||
onPlaylistClick = { playlistRoute(it) },
|
onPlaylistClick = { playlistRoute(it) },
|
||||||
|
onSearchClick = { searchRoute("") }
|
||||||
|
)
|
||||||
|
1 -> HomeSongs(
|
||||||
|
onSearchClick = { searchRoute("") }
|
||||||
)
|
)
|
||||||
1 -> HomeSongs()
|
|
||||||
2 -> HomePlaylists(
|
2 -> HomePlaylists(
|
||||||
onBuiltInPlaylist = { builtInPlaylistRoute(it) },
|
onBuiltInPlaylist = { builtInPlaylistRoute(it) },
|
||||||
onPlaylistClick = { localPlaylistRoute(it.id) }
|
onPlaylistClick = { localPlaylistRoute(it.id) },
|
||||||
|
onSearchClick = { searchRoute("") }
|
||||||
|
)
|
||||||
|
3 -> HomeArtistList(
|
||||||
|
onArtistClick = { artistRoute(it.id) },
|
||||||
|
onSearchClick = { searchRoute("") }
|
||||||
|
)
|
||||||
|
4 -> HomeAlbums(
|
||||||
|
onAlbumClick = { albumRoute(it.id) },
|
||||||
|
onSearchClick = { searchRoute("") }
|
||||||
)
|
)
|
||||||
3 -> HomeArtistList(onArtistClick = { artistRoute(it.id) })
|
|
||||||
4 -> HomeAlbums(onAlbumClick = { albumRoute(it.id) })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.offset
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
@@ -37,10 +36,10 @@ import it.vfsfitvnm.vimusic.enums.SortOrder
|
|||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.ScrollToTop
|
|
||||||
import it.vfsfitvnm.vimusic.ui.items.SongItem
|
import it.vfsfitvnm.vimusic.ui.items.SongItem
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
|
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
@@ -62,7 +61,9 @@ import kotlinx.coroutines.flow.flowOn
|
|||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeSongs() {
|
fun HomeSongs(
|
||||||
|
onSearchClick: () -> Unit
|
||||||
|
) {
|
||||||
val (colorPalette, typography, thumbnailShape) = LocalAppearance.current
|
val (colorPalette, typography, thumbnailShape) = LocalAppearance.current
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
@@ -187,11 +188,10 @@ fun HomeSongs() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollToTop(
|
FloatingActionsContainerWithScrollToTop(
|
||||||
lazyListState = lazyListState,
|
lazyListState = lazyListState,
|
||||||
modifier = Modifier
|
iconId = R.drawable.search,
|
||||||
.offset(x = Dimensions.navigationRailIconOffset - Dimensions.navigationRailWidth)
|
onClick = onSearchClick
|
||||||
.align(Alignment.BottomStart)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import it.vfsfitvnm.vimusic.savers.nullableSaver
|
|||||||
import it.vfsfitvnm.vimusic.savers.resultSaver
|
import it.vfsfitvnm.vimusic.savers.resultSaver
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
||||||
@@ -81,6 +82,7 @@ fun QuickPicks(
|
|||||||
onAlbumClick: (String) -> Unit,
|
onAlbumClick: (String) -> Unit,
|
||||||
onArtistClick: (String) -> Unit,
|
onArtistClick: (String) -> Unit,
|
||||||
onPlaylistClick: (String) -> Unit,
|
onPlaylistClick: (String) -> Unit,
|
||||||
|
onSearchClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val (colorPalette, typography) = LocalAppearance.current
|
val (colorPalette, typography) = LocalAppearance.current
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
@@ -129,6 +131,8 @@ fun QuickPicks(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
BoxWithConstraints {
|
BoxWithConstraints {
|
||||||
val itemInHorizontalGridWidth = maxWidth * quickPicksLazyGridItemWidthFactor
|
val itemInHorizontalGridWidth = maxWidth * quickPicksLazyGridItemWidthFactor
|
||||||
|
|
||||||
@@ -136,7 +140,7 @@ fun QuickPicks(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(colorPalette.background0)
|
.background(colorPalette.background0)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(scrollState)
|
||||||
.padding(LocalPlayerAwarePaddingValues.current)
|
.padding(LocalPlayerAwarePaddingValues.current)
|
||||||
) {
|
) {
|
||||||
Header(title = "Quick picks")
|
Header(title = "Quick picks")
|
||||||
@@ -345,5 +349,11 @@ fun QuickPicks(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FloatingActionsContainerWithScrollToTop(
|
||||||
|
scrollState = scrollState,
|
||||||
|
iconId = R.drawable.search,
|
||||||
|
onClick = onSearchClick
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ import it.vfsfitvnm.vimusic.savers.nullableSaver
|
|||||||
import it.vfsfitvnm.vimusic.transaction
|
import it.vfsfitvnm.vimusic.transaction
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
|
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.IconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.IconButton
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.InPlaylistMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.InPlaylistMediaItemMenu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Menu
|
import it.vfsfitvnm.vimusic.ui.components.themed.Menu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry
|
import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.PrimaryButton
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
|
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
|
||||||
import it.vfsfitvnm.vimusic.ui.items.SongItem
|
import it.vfsfitvnm.vimusic.ui.items.SongItem
|
||||||
@@ -274,16 +274,17 @@ fun LocalPlaylistSongs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimaryButton(
|
FloatingActionsContainerWithScrollToTop(
|
||||||
|
lazyListState = lazyListState,
|
||||||
iconId = R.drawable.shuffle,
|
iconId = R.drawable.shuffle,
|
||||||
isEnabled = playlistWithSongs?.songs?.isNotEmpty() == true,
|
|
||||||
onClick = {
|
onClick = {
|
||||||
playlistWithSongs?.songs
|
playlistWithSongs?.songs?.let { songs ->
|
||||||
?.shuffled()
|
if (songs.isNotEmpty()) {
|
||||||
?.map(DetailedSong::asMediaItem)
|
|
||||||
?.let { mediaItems ->
|
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(mediaItems)
|
binder?.player?.forcePlayFromBeginning(
|
||||||
|
songs.shuffled().map(DetailedSong::asMediaItem)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.saveable.autoSaver
|
import androidx.compose.runtime.saveable.autoSaver
|
||||||
@@ -29,12 +30,12 @@ import it.vfsfitvnm.vimusic.savers.nullableSaver
|
|||||||
import it.vfsfitvnm.vimusic.transaction
|
import it.vfsfitvnm.vimusic.transaction
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderPlaceholder
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderPlaceholder
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.LayoutWithAdaptiveThumbnail
|
import it.vfsfitvnm.vimusic.ui.components.themed.LayoutWithAdaptiveThumbnail
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.PrimaryButton
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.adaptiveThumbnailContent
|
import it.vfsfitvnm.vimusic.ui.components.themed.adaptiveThumbnailContent
|
||||||
import it.vfsfitvnm.vimusic.ui.items.SongItem
|
import it.vfsfitvnm.vimusic.ui.items.SongItem
|
||||||
@@ -62,7 +63,7 @@ import kotlinx.coroutines.withContext
|
|||||||
fun PlaylistSongList(
|
fun PlaylistSongList(
|
||||||
browseId: String,
|
browseId: String,
|
||||||
) {
|
) {
|
||||||
val (colorPalette, typography, thumbnailShape) = LocalAppearance.current
|
val (colorPalette) = LocalAppearance.current
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
@@ -162,9 +163,12 @@ fun PlaylistSongList(
|
|||||||
|
|
||||||
val thumbnailContent = adaptiveThumbnailContent(playlistPage == null, playlistPage?.thumbnail?.url)
|
val thumbnailContent = adaptiveThumbnailContent(playlistPage == null, playlistPage?.thumbnail?.url)
|
||||||
|
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
||||||
LayoutWithAdaptiveThumbnail(thumbnailContent = thumbnailContent) {
|
LayoutWithAdaptiveThumbnail(thumbnailContent = thumbnailContent) {
|
||||||
Box {
|
Box {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
state = lazyListState,
|
||||||
contentPadding = LocalPlayerAwarePaddingValues.current,
|
contentPadding = LocalPlayerAwarePaddingValues.current,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(colorPalette.background0)
|
.background(colorPalette.background0)
|
||||||
@@ -219,13 +223,17 @@ fun PlaylistSongList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimaryButton(
|
FloatingActionsContainerWithScrollToTop(
|
||||||
|
lazyListState = lazyListState,
|
||||||
iconId = R.drawable.shuffle,
|
iconId = R.drawable.shuffle,
|
||||||
isEnabled = playlistPage?.songsPage?.items?.isNotEmpty() == true,
|
|
||||||
onClick = {
|
onClick = {
|
||||||
playlistPage?.songsPage?.items?.map(Innertube.SongItem::asMediaItem)?.let { mediaItems ->
|
playlistPage?.songsPage?.items?.let { songs ->
|
||||||
|
if (songs.isNotEmpty()) {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(mediaItems.shuffled())
|
binder?.player?.forcePlayFromBeginning(
|
||||||
|
songs.shuffled().map(Innertube.SongItem::asMediaItem)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package it.vfsfitvnm.vimusic.ui.screens.search
|
|||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -21,6 +23,7 @@ import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
|||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
||||||
@@ -65,7 +68,11 @@ fun LocalSongSearch(
|
|||||||
val thumbnailSizeDp = Dimensions.thumbnails.song
|
val thumbnailSizeDp = Dimensions.thumbnails.song
|
||||||
val thumbnailSizePx = thumbnailSizeDp.px
|
val thumbnailSizePx = thumbnailSizeDp.px
|
||||||
|
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
||||||
|
Box {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
state = lazyListState,
|
||||||
contentPadding = LocalPlayerAwarePaddingValues.current,
|
contentPadding = LocalPlayerAwarePaddingValues.current,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -129,4 +136,7 @@ fun LocalSongSearch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FloatingActionsContainerWithScrollToTop(lazyListState = lazyListState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package it.vfsfitvnm.vimusic.ui.screens.search
|
package it.vfsfitvnm.vimusic.ui.screens.search
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
@@ -12,6 +13,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.text.BasicText
|
import androidx.compose.foundation.text.BasicText
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
@@ -45,6 +47,7 @@ import it.vfsfitvnm.vimusic.query
|
|||||||
import it.vfsfitvnm.vimusic.savers.SearchQuerySaver
|
import it.vfsfitvnm.vimusic.savers.SearchQuerySaver
|
||||||
import it.vfsfitvnm.vimusic.savers.listSaver
|
import it.vfsfitvnm.vimusic.savers.listSaver
|
||||||
import it.vfsfitvnm.vimusic.savers.resultSaver
|
import it.vfsfitvnm.vimusic.savers.resultSaver
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
@@ -62,6 +65,7 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun OnlineSearch(
|
fun OnlineSearch(
|
||||||
textFieldValue: TextFieldValue,
|
textFieldValue: TextFieldValue,
|
||||||
@@ -112,7 +116,11 @@ fun OnlineSearch(
|
|||||||
FocusRequester()
|
FocusRequester()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
||||||
|
Box {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
state = lazyListState,
|
||||||
contentPadding = LocalPlayerAwarePaddingValues.current,
|
contentPadding = LocalPlayerAwarePaddingValues.current,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -302,6 +310,9 @@ fun OnlineSearch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FloatingActionsContainerWithScrollToTop(lazyListState = lazyListState)
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
delay(300)
|
delay(300)
|
||||||
focusRequester.requestFocus()
|
focusRequester.requestFocus()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package it.vfsfitvnm.vimusic.ui.screens.searchresult
|
package it.vfsfitvnm.vimusic.ui.screens.searchresult
|
||||||
|
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -19,6 +20,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues
|
import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues
|
||||||
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
import it.vfsfitvnm.vimusic.utils.center
|
import it.vfsfitvnm.vimusic.utils.center
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||||
@@ -70,6 +72,7 @@ inline fun <T : Innertube.Item> ItemsPage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Box {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = lazyListState,
|
state = lazyListState,
|
||||||
contentPadding = LocalPlayerAwarePaddingValues.current,
|
contentPadding = LocalPlayerAwarePaddingValues.current,
|
||||||
@@ -117,4 +120,7 @@ inline fun <T : Innertube.Item> ItemsPage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FloatingActionsContainerWithScrollToTop(lazyListState = lazyListState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package it.vfsfitvnm.vimusic.utils
|
||||||
|
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
|
||||||
|
suspend fun LazyGridState.smoothScrollToTop() {
|
||||||
|
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
|
||||||
|
scrollToItem(layoutInfo.visibleItemsInfo.size)
|
||||||
|
}
|
||||||
|
animateScrollToItem(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LazyGridState.isScrollingDownToIsFar(): Pair<Boolean, Boolean> {
|
||||||
|
var previousIndex by remember(this) {
|
||||||
|
mutableStateOf(firstVisibleItemIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousScrollOffset by remember(this) {
|
||||||
|
mutableStateOf(firstVisibleItemScrollOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return remember(this) {
|
||||||
|
derivedStateOf {
|
||||||
|
if (previousIndex != firstVisibleItemIndex) {
|
||||||
|
previousIndex > firstVisibleItemIndex
|
||||||
|
} else {
|
||||||
|
previousScrollOffset >= firstVisibleItemScrollOffset
|
||||||
|
}.also {
|
||||||
|
previousIndex = firstVisibleItemIndex
|
||||||
|
previousScrollOffset = firstVisibleItemScrollOffset
|
||||||
|
} to (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size)
|
||||||
|
}
|
||||||
|
}.value
|
||||||
|
}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
package it.vfsfitvnm.vimusic.utils
|
package it.vfsfitvnm.vimusic.utils
|
||||||
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
|
||||||
suspend fun LazyListState.smoothScrollToTop() {
|
suspend fun LazyListState.smoothScrollToTop() {
|
||||||
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
|
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
|
||||||
@@ -8,3 +14,27 @@ suspend fun LazyListState.smoothScrollToTop() {
|
|||||||
}
|
}
|
||||||
animateScrollToItem(0)
|
animateScrollToItem(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LazyListState.isScrollingDownToIsFar(): Pair<Boolean, Boolean> {
|
||||||
|
var previousIndex by remember(this) {
|
||||||
|
mutableStateOf(firstVisibleItemIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousScrollOffset by remember(this) {
|
||||||
|
mutableStateOf(firstVisibleItemScrollOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return remember(this) {
|
||||||
|
derivedStateOf {
|
||||||
|
if (previousIndex != firstVisibleItemIndex) {
|
||||||
|
previousIndex > firstVisibleItemIndex
|
||||||
|
} else {
|
||||||
|
previousScrollOffset >= firstVisibleItemScrollOffset
|
||||||
|
}.also {
|
||||||
|
previousIndex = firstVisibleItemIndex
|
||||||
|
previousScrollOffset = firstVisibleItemScrollOffset
|
||||||
|
} to (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size)
|
||||||
|
}
|
||||||
|
}.value
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package it.vfsfitvnm.vimusic.utils
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ScrollState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ScrollState.isScrollingDown(): Boolean {
|
||||||
|
var previousValue by remember(this) {
|
||||||
|
mutableStateOf(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return remember(this) {
|
||||||
|
derivedStateOf {
|
||||||
|
(previousValue >= value).also {
|
||||||
|
previousValue = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.value
|
||||||
|
}
|
||||||
13
app/src/main/res/drawable/chevron_up.xml
Normal file
13
app/src/main/res/drawable/chevron_up.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="512dp"
|
||||||
|
android:height="512dp"
|
||||||
|
android:viewportWidth="512"
|
||||||
|
android:viewportHeight="512">
|
||||||
|
<path
|
||||||
|
android:pathData="M112,328l144,-144l144,144"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="48"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
Reference in New Issue
Block a user