For #21120 - ImageLoader with a shimmer effect placeholder

upstream-sync
Mugurell 3 years ago committed by mergify[bot]
parent abc881f9f8
commit a54a4ea20a

@ -272,7 +272,8 @@ class SessionControlAdapter(
when (viewType) { when (viewType) {
PocketStoriesViewHolder.LAYOUT_ID -> return PocketStoriesViewHolder( PocketStoriesViewHolder.LAYOUT_ID -> return PocketStoriesViewHolder(
ComposeView(parent.context), ComposeView(parent.context),
store store,
components.core.client
) )
} }

@ -10,8 +10,10 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.pocket
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
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.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -43,6 +45,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
@ -52,8 +55,18 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import mozilla.components.concept.fetch.Client
import mozilla.components.concept.fetch.MutableHeaders
import mozilla.components.concept.fetch.Request
import mozilla.components.concept.fetch.Response
import mozilla.components.service.pocket.PocketRecommendedStory import mozilla.components.service.pocket.PocketRecommendedStory
import mozilla.components.support.images.compose.loader.Fallback
import mozilla.components.support.images.compose.loader.ImageLoader
import mozilla.components.support.images.compose.loader.Placeholder
import mozilla.components.support.images.compose.loader.WithImage
import mozilla.components.ui.colors.PhotonColors
import org.mozilla.fenix.R import org.mozilla.fenix.R
import kotlin.math.roundToInt
import kotlin.random.Random import kotlin.random.Random
/** /**
@ -62,6 +75,7 @@ import kotlin.random.Random
@Composable @Composable
fun PocketStory( fun PocketStory(
@PreviewParameter(PocketStoryProvider::class) story: PocketRecommendedStory, @PreviewParameter(PocketStoryProvider::class) story: PocketRecommendedStory,
client: Client,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Column( Column(
@ -73,20 +87,53 @@ fun PocketStory(
Card( Card(
elevation = 6.dp, elevation = 6.dp,
shape = RoundedCornerShape(4.dp), shape = RoundedCornerShape(4.dp),
modifier = Modifier modifier = Modifier.size(160.dp, 87.dp)
.size(160.dp, 87.dp)
.padding(bottom = 8.dp)
) { ) {
// Don't yet have a easy way to load URLs in Images. ImageLoader(
// Default to a solid color to make it easy to appreciate dimensions client = client,
Box(Modifier.background(Color.Blue)) // The endpoint allows us to ask for the optimal resolution image.
// Image( url = story.imageUrl.replace(
// painterResource(R.drawable.ic_pdd), "{wh}",
// contentDescription = "hero image", with(LocalDensity.current) {
// contentScale = ContentScale.FillHeight, "${160.dp.toPx().roundToInt()}x${87.dp.toPx().roundToInt()}"
// ) }
),
targetSize = 160.dp
) {
WithImage { painter ->
Image(
painter,
modifier = Modifier.size(160.dp, 87.dp),
contentDescription = "${story.title} story image"
)
}
Placeholder {
Box(
Modifier.background(
when (isSystemInDarkTheme()) {
true -> Color(0xFF42414D) // DarkGrey30
false -> PhotonColors.LightGrey30
}
)
)
}
Fallback {
Box(
Modifier.background(
when (isSystemInDarkTheme()) {
true -> Color(0xFF42414D) // DarkGrey30
false -> PhotonColors.LightGrey30
}
)
)
}
}
} }
Spacer(modifier = Modifier.height(8.dp))
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text( Text(
modifier = Modifier.padding(bottom = 2.dp), modifier = Modifier.padding(bottom = 2.dp),
@ -110,7 +157,8 @@ fun PocketStory(
*/ */
@Composable @Composable
fun PocketStories( fun PocketStories(
@PreviewParameter(PocketStoryProvider::class) stories: List<PocketRecommendedStory> @PreviewParameter(PocketStoryProvider::class) stories: List<PocketRecommendedStory>,
client: Client
) { ) {
// Items will be shown on two rows. Ceil the divide result to show more items on the top row. // Items will be shown on two rows. Ceil the divide result to show more items on the top row.
val halfStoriesIndex = (stories.size + 1) / 2 val halfStoriesIndex = (stories.size + 1) / 2
@ -121,12 +169,12 @@ fun PocketStories(
Column( Column(
Modifier.padding(end = if (index == halfStoriesIndex) 0.dp else 8.dp) Modifier.padding(end = if (index == halfStoriesIndex) 0.dp else 8.dp)
) { ) {
PocketStory(item) PocketStory(item, client)
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
stories.getOrNull(halfStoriesIndex + index)?.let { stories.getOrNull(halfStoriesIndex + index)?.let {
PocketStory(it) PocketStory(it, client)
} }
} }
} }
@ -170,15 +218,6 @@ fun PocketRecommendations(
) { ) {
content() content()
// Image(
// painterResource(R.drawable.ic_firefox_pocket),
// "Firefox and Pocket logos",
// Modifier
// .size(64.dp, 27.dp)
// .padding(top = 16.dp),
// contentScale = ContentScale.FillHeight
// )
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
ClickableText( ClickableText(
text = annotatedText, text = annotatedText,
@ -245,7 +284,10 @@ fun ExpandableCard(content: @Composable (() -> Unit)) {
private fun FinalDesign() { private fun FinalDesign() {
ExpandableCard { ExpandableCard {
PocketRecommendations { PocketRecommendations {
PocketStories(stories = getFakePocketStories(7)) PocketStories(
stories = getFakePocketStories(7),
client = FakeClient()
)
} }
} }
} }
@ -273,3 +315,12 @@ private fun getFakePocketStories(limit: Int = 1): List<PocketRecommendedStory> {
} }
} }
} }
private class FakeClient : Client() {
override fun fetch(request: Request) = Response(
url = request.url,
status = 200,
body = Response.Body.empty(),
headers = MutableHeaders()
)
}

@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import mozilla.components.concept.fetch.Client
import mozilla.components.lib.state.ext.observeAsComposableState import mozilla.components.lib.state.ext.observeAsComposableState
import mozilla.components.service.pocket.PocketRecommendedStory import mozilla.components.service.pocket.PocketRecommendedStory
import org.mozilla.fenix.home.HomeFragmentStore import org.mozilla.fenix.home.HomeFragmentStore
@ -24,7 +25,8 @@ private const val STORIES_TO_SHOW_COUNT = 7
*/ */
class PocketStoriesViewHolder( class PocketStoriesViewHolder(
val composeView: ComposeView, val composeView: ComposeView,
val store: HomeFragmentStore val store: HomeFragmentStore,
val client: Client
) : RecyclerView.ViewHolder(composeView) { ) : RecyclerView.ViewHolder(composeView) {
init { init {
@ -32,7 +34,7 @@ class PocketStoriesViewHolder(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
) )
composeView.setContent { composeView.setContent {
PocketStories(store) PocketStories(store, client)
} }
} }
@ -42,14 +44,20 @@ class PocketStoriesViewHolder(
} }
@Composable @Composable
fun PocketStories(store: HomeFragmentStore) { fun PocketStories(
store: HomeFragmentStore,
client: Client
) {
val stories = store val stories = store
.observeAsComposableState { state -> state.pocketArticles }.value .observeAsComposableState { state -> state.pocketArticles }.value
?.take(STORIES_TO_SHOW_COUNT) ?.take(STORIES_TO_SHOW_COUNT)
ExpandableCard { ExpandableCard {
PocketRecommendations { PocketRecommendations {
PocketStories(stories ?: emptyList()) PocketStories(
stories ?: emptyList(),
client
)
} }
} }
} }

Loading…
Cancel
Save