Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Reader] Implement "Tags" feed horizontal posts list item component loading #20634

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.wordpress.android.ui.compose.components.shimmer

import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import org.wordpress.android.ui.compose.theme.AppColor

/**
* Taken from https://github.com/canerkaseler/jetpack-compose-shimmer-loading-animation
*/
internal fun Modifier.shimmerLoadingAnimation(
isLoadingCompleted: Boolean = true,
isLightModeActive: Boolean = true,
widthOfShadowBrush: Int = 500,
angleOfAxisY: Float = 270f,
durationMillis: Int = 1000,
): Modifier {
if (isLoadingCompleted) {
return this
} else {
return composed {
val shimmerColors = ShimmerAnimationData(isLightMode = isLightModeActive).getColours()

val transition = rememberInfiniteTransition(label = "")

val translateAnimation = transition.animateFloat(
initialValue = 0f,
targetValue = (durationMillis + widthOfShadowBrush).toFloat(),
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = durationMillis,
easing = LinearEasing,
),
repeatMode = RepeatMode.Restart,
),
label = "Shimmer loading animation",
)

this.background(
brush = Brush.linearGradient(
colors = shimmerColors,
start = Offset(x = translateAnimation.value - widthOfShadowBrush, y = 0.0f),
end = Offset(x = translateAnimation.value, y = angleOfAxisY),
),
)
}
}
}

internal data class ShimmerAnimationData(
private val isLightMode: Boolean
) {
fun getColours(): List<Color> {
return if (isLightMode) {
val color = AppColor.White

listOf(
color.copy(alpha = 0.3f),
color.copy(alpha = 0.5f),
color.copy(alpha = 1.0f),
color.copy(alpha = 0.5f),
color.copy(alpha = 0.3f),
)
} else {
val color = AppColor.Black

listOf(
color.copy(alpha = 0.0f),
color.copy(alpha = 0.3f),
color.copy(alpha = 0.5f),
color.copy(alpha = 0.3f),
color.copy(alpha = 0.0f),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.wordpress.android.ui.compose.components.shimmer

import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

@Composable
fun ShimmerBox(
modifier: Modifier = Modifier,
isLoadingCompleted: Boolean = true,
isLightModeActive: Boolean = true,
widthOfShadowBrush: Int = 500,
angleOfAxisY: Float = 270f,
durationMillis: Int = 1000,
) {
Box(
modifier = modifier
.shimmerLoadingAnimation(
isLoadingCompleted = isLoadingCompleted,
isLightModeActive = isLightModeActive,
widthOfShadowBrush = widthOfShadowBrush,
angleOfAxisY = angleOfAxisY,
durationMillis = durationMillis,
)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package org.wordpress.android.ui.reader.views.compose.horizontalpostlist

import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.shape.RoundedCornerShape
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.wordpress.android.ui.compose.components.shimmer.ShimmerBox
import org.wordpress.android.ui.compose.theme.AppColor
import org.wordpress.android.ui.compose.theme.AppTheme
import org.wordpress.android.ui.compose.unit.Margin

@Composable
fun HorizontalPostListItemLoading() {
val loadingColor = AppColor.Black.copy(
alpha = 0.08F
)
Column(
modifier = Modifier
.width(240.dp)
.height(340.dp),
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
ShimmerBox(
modifier = Modifier
.width(99.dp)
.height(8.dp)
.clip(shape = RoundedCornerShape(16.dp))
.background(loadingColor),
isLoadingCompleted = false,
)
}
ShimmerBox(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe nitpick - I see creation of ShimmerBox instances with similar modifiers. Is there a way to further abstract these calls?

Copy link
Contributor Author

@RenanLukas RenanLukas Apr 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniloercoli I think the color and shape repeats in many cases. The shape is not always the same, though, so maybe I can move the color to the ShimmerBox component? WDYT?

(Edit)
I've extracted the shimmer background color to the generic component.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Feel free to merge this PR.

modifier = Modifier
.padding(top = Margin.Large.value)
.width(204.dp)
.height(18.dp)
.clip(shape = RoundedCornerShape(16.dp))
.background(loadingColor),
isLoadingCompleted = false,
)
ShimmerBox(
modifier = Modifier
.padding(top = Margin.Large.value)
.width(140.dp)
.height(18.dp)
.clip(shape = RoundedCornerShape(16.dp))
.background(loadingColor),
isLoadingCompleted = false,
)
ShimmerBox(
modifier = Modifier
.padding(top = Margin.Large.value)
.fillMaxWidth()
.height(150.dp)
.clip(shape = RoundedCornerShape(8.dp))
.background(loadingColor),
isLoadingCompleted = false,
)
ShimmerBox(
modifier = Modifier
.padding(
start = Margin.Small.value,
top = Margin.Large.value,
)
.width(170.dp)
.height(8.dp)
.clip(shape = RoundedCornerShape(16.dp))
.background(loadingColor),
isLoadingCompleted = false,
)
ShimmerBox(
modifier = Modifier
.padding(
start = Margin.Small.value,
top = Margin.Large.value,
)
.width(170.dp)
.height(8.dp)
.clip(shape = RoundedCornerShape(16.dp))
.background(loadingColor),
isLoadingCompleted = false,
)
}
}

@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun HorizontalPostListItemLoadingPreview() {
AppTheme {
Box(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
LazyRow(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp, bottom = 16.dp),
contentPadding = PaddingValues(horizontal = 12.dp),
) {
item {
HorizontalPostListItemLoading()
Spacer(Modifier.width(12.dp))
HorizontalPostListItemLoading()
Spacer(Modifier.width(12.dp))
HorizontalPostListItemLoading()
Spacer(Modifier.width(12.dp))
HorizontalPostListItemLoading()
}
}
}
}
}
Loading