Tweak code

This commit is contained in:
vfsfitvnm 2022-09-28 15:43:42 +02:00
parent 6a69eb57e9
commit 752b29c93a
11 changed files with 173 additions and 189 deletions

View file

@ -1,105 +0,0 @@
package it.vfsfitvnm.vimusic.ui.components
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.layout
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
@Composable
fun TabColumn(
tabIndex: Int,
onTabIndexChanged: (Int) -> Unit,
selectedTextColor: Color,
disabledTextColor: Color,
textStyle: TextStyle,
modifier: Modifier = Modifier,
content: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit
) {
Column(
modifier = modifier
.verticalScroll(rememberScrollState())
) {
val transition = updateTransition(targetState = tabIndex, label = null)
content { index, text, icon ->
val dothAlpha by transition.animateFloat(label = "") {
if (it == index) 1f else 0f
}
val textColor by transition.animateColor(label = "") {
if (it == index) selectedTextColor else disabledTextColor
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = { onTabIndexChanged(index) }
)
.padding(horizontal = 8.dp)
) {
Image(
painter = painterResource(icon),
contentDescription = null,
colorFilter = ColorFilter.tint(selectedTextColor),
modifier = Modifier
.vertical()
.graphicsLayer {
alpha = dothAlpha
translationX = (1f - dothAlpha) * -48.dp.toPx()
rotationZ = -90f
}
.size(12.dp)
)
BasicText(
text = text,
style = textStyle.copy(color = textColor),
modifier = Modifier
.vertical()
.rotate(-90f)
.padding(horizontal = 16.dp)
)
}
}
}
}
fun Modifier.vertical() =
layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.height, placeable.width) {
placeable.place(
x = -(placeable.width / 2 - placeable.height / 2),
y = -(placeable.height / 2 - placeable.width / 2)
)
}
}

View file

@ -18,6 +18,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.shimmer import it.vfsfitvnm.vimusic.ui.styling.shimmer
import it.vfsfitvnm.vimusic.utils.medium import it.vfsfitvnm.vimusic.utils.medium
@ -55,7 +56,7 @@ fun Header(
horizontalAlignment = Alignment.End, horizontalAlignment = Alignment.End,
modifier = modifier modifier = modifier
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
.height(128.dp) .height(Dimensions.headerHeight)
.fillMaxWidth() .fillMaxWidth()
) { ) {
Spacer( Spacer(

View file

@ -0,0 +1,134 @@
package it.vfsfitvnm.vimusic.ui.components.themed
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.layout
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.semiBold
@Composable
fun NavigationRail(
topIconButtonId: Int,
onTopIconButtonClick: () -> Unit,
tabIndex: Int,
onTabIndexChanged: (Int) -> Unit,
content: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit,
modifier: Modifier = Modifier
) {
val (colorPalette, typography) = LocalAppearance.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
) {
Box(
contentAlignment = Alignment.TopCenter,
modifier = Modifier
.size(width = Dimensions.navigationRailWidth, height = Dimensions.headerHeight)
) {
Image(
painter = painterResource(topIconButtonId),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.textSecondary),
modifier = Modifier
.offset(x = Dimensions.navigationRailIconOffset, y = 48.dp)
.clip(CircleShape)
.clickable(onClick = onTopIconButtonClick)
.padding(all = 12.dp)
.size(22.dp)
)
}
Column(
modifier = modifier
.verticalScroll(rememberScrollState())
) {
val transition = updateTransition(targetState = tabIndex, label = null)
content { index, text, icon ->
val dothAlpha by transition.animateFloat(label = "") {
if (it == index) 1f else 0f
}
val textColor by transition.animateColor(label = "") {
if (it == index) colorPalette.text else colorPalette.textDisabled
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(24.dp))
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = { onTabIndexChanged(index) }
)
.padding(horizontal = 8.dp)
) {
Image(
painter = painterResource(icon),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.vertical()
.graphicsLayer {
alpha = dothAlpha
translationX = (1f - dothAlpha) * -48.dp.toPx()
rotationZ = -90f
}
.size(Dimensions.navigationRailIconOffset * 2)
)
BasicText(
text = text,
style = typography.xs.semiBold.copy(color = textColor),
modifier = Modifier
.vertical()
.rotate(-90f)
.padding(horizontal = 16.dp)
)
}
}
}
}
}
private fun Modifier.vertical() =
layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.height, placeable.width) {
placeable.place(
x = -(placeable.width / 2 - placeable.height / 2),
y = -(placeable.height / 2 - placeable.width / 2)
)
}
}

View file

@ -13,6 +13,7 @@ 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.ColumnScope
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -37,7 +38,7 @@ fun Scaffold(
onTopIconButtonClick: () -> Unit, onTopIconButtonClick: () -> Unit,
tabIndex: Int, tabIndex: Int,
onTabChanged: (Int) -> Unit, onTabChanged: (Int) -> Unit,
tabColumnContent: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit, tabColumnContent: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit,
primaryIconButtonId: Int? = null, primaryIconButtonId: Int? = null,
onPrimaryIconButtonClick: () -> Unit = {}, onPrimaryIconButtonClick: () -> Unit = {},
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -54,14 +55,14 @@ fun Scaffold(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
) { ) {
VerticalBar( NavigationRail(
topIconButtonId = topIconButtonId, topIconButtonId = topIconButtonId,
onTopIconButtonClick = onTopIconButtonClick, onTopIconButtonClick = onTopIconButtonClick,
tabIndex = tabIndex, tabIndex = tabIndex,
onTabChanged = onTabChanged, onTabIndexChanged = onTabChanged,
tabColumnContent = tabColumnContent,
modifier = Modifier modifier = Modifier
.padding(LocalPlayerAwarePaddingValues.current) .padding(LocalPlayerAwarePaddingValues.current),
content = tabColumnContent
) )
AnimatedContent( AnimatedContent(

View file

@ -1,66 +0,0 @@
package it.vfsfitvnm.vimusic.ui.components.themed
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
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.components.TabColumn
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.semiBold
@Composable
fun VerticalBar(
topIconButtonId: Int,
onTopIconButtonClick: () -> Unit,
tabIndex: Int,
onTabChanged: (Int) -> Unit,
tabColumnContent: @Composable (@Composable (Int, String, Int) -> Unit) -> Unit,
modifier: Modifier = Modifier
) {
val (colorPalette, typography) = LocalAppearance.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
.padding(vertical = 16.dp)
) {
Image(
painter = painterResource(topIconButtonId),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.textSecondary),
modifier = Modifier
.clip(CircleShape)
.clickable(onClick = onTopIconButtonClick)
.padding(all = 12.dp)
.size(22.dp)
)
Spacer(
modifier = Modifier
.width(Dimensions.verticalBarWidth)
.height(32.dp)
)
TabColumn(
tabIndex = tabIndex,
onTabIndexChanged = onTabChanged,
selectedTextColor = colorPalette.text,
disabledTextColor = colorPalette.textDisabled,
textStyle = typography.xs.semiBold,
content = tabColumnContent,
)
}
}

View file

@ -130,7 +130,7 @@ fun AlbumOverview(
} }
BoxWithConstraints { BoxWithConstraints {
val thumbnailSizeDp = maxWidth - Dimensions.verticalBarWidth val thumbnailSizeDp = maxWidth - Dimensions.navigationRailWidth
val thumbnailSizePx = (thumbnailSizeDp - 32.dp).px val thumbnailSizePx = (thumbnailSizeDp - 32.dp).px
albumResult?.getOrNull()?.let { album -> albumResult?.getOrNull()?.let { album ->

View file

@ -210,7 +210,7 @@ fun HomeSongList() {
ScrollToTop( ScrollToTop(
lazyListState = lazyListState, lazyListState = lazyListState,
modifier = Modifier modifier = Modifier
.offset(x = -Dimensions.verticalBarWidth) .offset(x = Dimensions.navigationRailIconOffset - Dimensions.navigationRailWidth)
.align(Alignment.BottomStart) .align(Alignment.BottomStart)
) )
} }

View file

@ -99,7 +99,7 @@ fun PlaylistSongList(
} }
BoxWithConstraints { BoxWithConstraints {
val thumbnailSizeDp = maxWidth - Dimensions.verticalBarWidth val thumbnailSizeDp = maxWidth - Dimensions.navigationRailWidth
val thumbnailSizePx = (thumbnailSizeDp - 32.dp).px val thumbnailSizePx = (thumbnailSizeDp - 32.dp).px
val songThumbnailSizeDp = Dimensions.thumbnails.song val songThumbnailSizeDp = Dimensions.thumbnails.song

View file

@ -49,9 +49,9 @@ import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.savers.SearchQueryListSaver import it.vfsfitvnm.vimusic.savers.SearchQueryListSaver
import it.vfsfitvnm.vimusic.savers.StringListResultSaver import it.vfsfitvnm.vimusic.savers.StringListResultSaver
import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.components.themed.Header
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.align import it.vfsfitvnm.vimusic.utils.align
import it.vfsfitvnm.vimusic.utils.center
import it.vfsfitvnm.vimusic.utils.medium import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.produceSaveableOneShotState import it.vfsfitvnm.vimusic.utils.produceSaveableOneShotState
import it.vfsfitvnm.vimusic.utils.produceSaveableState import it.vfsfitvnm.vimusic.utils.produceSaveableState
@ -320,7 +320,17 @@ fun OnlineSearch(
} }
} ?: suggestionsResult?.exceptionOrNull()?.let { throwable -> } ?: suggestionsResult?.exceptionOrNull()?.let { throwable ->
item { item {
LoadingOrError(errorMessage = throwable.javaClass.canonicalName) {} Box(
modifier = Modifier
.fillMaxSize()
) {
BasicText(
text = "An error has occurred.",
style = typography.s.medium.secondary.center,
modifier = Modifier
.align(Alignment.Center)
)
}
} }
} }
} }

View file

@ -4,6 +4,7 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxScope
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.LazyItemScope import androidx.compose.foundation.lazy.LazyItemScope
@ -19,6 +20,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import com.valentinilk.shimmer.shimmer
import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues
import it.vfsfitvnm.vimusic.savers.ListSaver import it.vfsfitvnm.vimusic.savers.ListSaver
import it.vfsfitvnm.vimusic.savers.StringResultSaver import it.vfsfitvnm.vimusic.savers.StringResultSaver
@ -141,6 +143,10 @@ inline fun <T : YouTube.Item> SearchResult(
} }
} }
} ?: item(key = "loading") { } ?: item(key = "loading") {
Column(
modifier = Modifier
.shimmer()
) {
repeat(if (items.isEmpty()) 8 else 3) { index -> repeat(if (items.isEmpty()) 8 else 3) { index ->
Box( Box(
modifier = Modifier modifier = Modifier
@ -151,3 +157,4 @@ inline fun <T : YouTube.Item> SearchResult(
} }
} }
} }
}

View file

@ -10,7 +10,9 @@ import androidx.compose.ui.unit.dp
object Dimensions { object Dimensions {
val itemsVerticalPadding = 8.dp val itemsVerticalPadding = 8.dp
val verticalBarWidth = 64.dp val navigationRailWidth = 64.dp
val navigationRailIconOffset = 6.dp
val headerHeight = 128.dp
object thumbnails { object thumbnails {
val album = 128.dp val album = 128.dp