|
|
@ -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()
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|