@ -4,19 +4,32 @@
package org.mozilla.fenix.shopping.ui
import androidx.compose.animation.Crossfade
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.spring
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
@ -25,7 +38,9 @@ import org.mozilla.fenix.compose.SwitchWithLabel
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.compose.button.SecondaryButton
import org.mozilla.fenix.shopping.store.ReviewQualityCheckState
import org.mozilla.fenix.shopping.store.ReviewQualityCheckState.HighlightType
import org.mozilla.fenix.shopping.store.ReviewQualityCheckState.OptedIn.ProductReviewState.AnalysisPresent
import org.mozilla.fenix.shopping.store.forCompactMode
import org.mozilla.fenix.theme.FirefoxTheme
/ * *
@ -60,6 +75,20 @@ fun ProductAnalysis(
modifier = Modifier . fillMaxWidth ( ) ,
)
if ( productAnalysis . highlights != null ) {
HighlightsCard (
highlights = productAnalysis . highlights ,
modifier = Modifier . fillMaxWidth ( ) ,
)
Text (
text = stringResource ( R . string . review _quality _check _highlights _caption ) ,
color = FirefoxTheme . colors . textPrimary ,
style = FirefoxTheme . typography . caption ,
modifier = Modifier . padding ( horizontal = 16. dp ) ,
)
}
SettingsCard (
modifier = Modifier . fillMaxWidth ( ) ,
productRecommendationsEnabled = productRecommendationsEnabled ,
@ -121,6 +150,129 @@ private fun AdjustedProductRatingCard(
}
}
@Composable
private fun HighlightsCard (
highlights : Map < HighlightType , List < String > > ,
modifier : Modifier = Modifier ,
) {
ReviewQualityCheckCard ( modifier = modifier ) {
var isExpanded by remember { mutableStateOf ( false ) }
val highlightsForCompactMode = remember ( highlights ) { highlights . forCompactMode ( ) }
val highlightsToDisplay = remember ( isExpanded , highlights ) {
if ( isExpanded ) {
highlights
} else {
highlightsForCompactMode
}
}
Text (
text = stringResource ( R . string . review _quality _check _highlights _title ) ,
color = FirefoxTheme . colors . textPrimary ,
style = FirefoxTheme . typography . headline8 ,
)
Spacer ( modifier = Modifier . height ( 16. dp ) )
Box (
contentAlignment = Alignment . BottomCenter ,
modifier = Modifier . fillMaxWidth ( ) ,
) {
Column (
modifier = Modifier
. fillMaxWidth ( )
. animateContentSize ( animationSpec = spring ( ) ) ,
) {
highlightsToDisplay . forEach { highlight ->
HighlightTitle ( highlight . key )
Spacer ( modifier = Modifier . height ( 8. dp ) )
highlight . value . forEach {
HighlightText ( it )
Spacer ( modifier = Modifier . height ( 4. dp ) )
}
if ( highlightsToDisplay . entries . last ( ) . key != highlight . key ) {
Spacer ( modifier = Modifier . height ( 16. dp ) )
}
}
}
Crossfade (
targetState = isExpanded ,
label = " HighlightsCard-Crossfade " ,
) { expanded ->
if ( expanded . not ( ) ) {
Spacer (
modifier = Modifier
. height ( 32. dp )
. fillMaxWidth ( )
. background (
brush = Brush . verticalGradient (
colors = listOf (
FirefoxTheme . colors . layer2 . copy ( alpha = 0f ) ,
FirefoxTheme . colors . layer2 ,
) ,
) ,
) ,
)
}
}
}
Spacer ( modifier = Modifier . height ( 8. dp ) )
SecondaryButton (
text = if ( isExpanded ) {
stringResource ( R . string . review _quality _check _highlights _show _less )
} else {
stringResource ( R . string . review _quality _check _highlights _show _more )
} ,
onClick = { isExpanded = isExpanded . not ( ) } ,
)
}
}
@Composable
private fun HighlightText ( text : String ) {
Row (
verticalAlignment = Alignment . CenterVertically ,
) {
Spacer ( modifier = Modifier . width ( 32. dp ) )
Text (
text = text ,
color = FirefoxTheme . colors . textPrimary ,
style = FirefoxTheme . typography . body2 ,
)
}
}
@Composable
private fun HighlightTitle ( highlightType : HighlightType ) {
Row (
verticalAlignment = Alignment . CenterVertically ,
) {
val highlight = remember ( highlightType ) { highlightType . toHighlight ( ) }
Icon (
painter = painterResource ( id = highlight . iconResourceId ) ,
tint = FirefoxTheme . colors . iconPrimary ,
contentDescription = null ,
)
Spacer ( modifier = Modifier . width ( 8. dp ) )
Text (
text = stringResource ( id = highlight . titleResourceId ) ,
color = FirefoxTheme . colors . textPrimary ,
style = FirefoxTheme . typography . headline8 ,
)
}
}
@Composable
private fun SettingsCard (
productRecommendationsEnabled : Boolean ,
@ -151,6 +303,42 @@ private fun SettingsCard(
}
}
private fun HighlightType . toHighlight ( ) =
when ( this ) {
HighlightType . QUALITY -> Highlight . QUALITY
HighlightType . PRICE -> Highlight . PRICE
HighlightType . SHIPPING -> Highlight . SHIPPING
HighlightType . PACKAGING _AND _APPEARANCE -> Highlight . PACKAGING _AND _APPEARANCE
HighlightType . COMPETITIVENESS -> Highlight . COMPETITIVENESS
}
// As part of Bug 1841600, update iconResourceId for each highlight type.
private enum class Highlight (
val titleResourceId : Int ,
val iconResourceId : Int ,
) {
QUALITY (
titleResourceId = R . string . review _quality _check _highlights _type _quality ,
iconResourceId = R . drawable . ic _shopping _cart ,
) ,
PRICE (
titleResourceId = R . string . review _quality _check _highlights _type _price ,
iconResourceId = R . drawable . ic _shopping _cart ,
) ,
SHIPPING (
titleResourceId = R . string . review _quality _check _highlights _type _shipping ,
iconResourceId = R . drawable . ic _shopping _cart ,
) ,
PACKAGING _AND _APPEARANCE (
titleResourceId = R . string . review _quality _check _highlights _type _packaging _appearance ,
iconResourceId = R . drawable . ic _shopping _cart ,
) ,
COMPETITIVENESS (
titleResourceId = R . string . review _quality _check _highlights _type _competitiveness ,
iconResourceId = R . drawable . ic _shopping _cart ,
) ,
}
@Composable
@LightDarkPreview
private fun ProductAnalysisPreview ( ) {
@ -169,27 +357,27 @@ private fun ProductAnalysisPreview() {
adjustedRating = 3.6f ,
productUrl = " 123 " ,
highlights = mapOf (
ReviewQualityCheckState. HighlightType. QUALITY to listOf (
HighlightType. QUALITY to listOf (
" High quality " ,
" Excellent craftsmanship " ,
" Superior materials " ,
) ,
ReviewQualityCheckState. HighlightType. PRICE to listOf (
HighlightType. PRICE to listOf (
" Affordable prices " ,
" Great value for money " ,
" Discounted offers " ,
) ,
ReviewQualityCheckState. HighlightType. SHIPPING to listOf (
HighlightType. SHIPPING to listOf (
" Fast and reliable shipping " ,
" Free shipping options " ,
" Express delivery " ,
) ,
ReviewQualityCheckState. HighlightType. PACKAGING _AND _APPEARANCE to listOf (
HighlightType. PACKAGING _AND _APPEARANCE to listOf (
" Elegant packaging " ,
" Attractive appearance " ,
" Beautiful design " ,
) ,
ReviewQualityCheckState. HighlightType. COMPETITIVENESS to listOf (
HighlightType. COMPETITIVENESS to listOf (
" Competitive pricing " ,
" Strong market presence " ,
" Unbeatable deals " ,