Bug 1840335 - Add product recommendation UI

fenix/121.0
Noah Bond 8 months ago committed by mergify[bot]
parent 51033cb791
commit b5adb3d54c

@ -64,5 +64,7 @@ class ReviewQualityCheckNavigationMiddleware(
is ReviewQualityCheckAction.OpenOnboardingPrivacyPolicyLink -> PRIVACY_POLICY_URL
is ReviewQualityCheckAction.OpenPoweredByLink -> POWERED_BY_URL
is ReviewQualityCheckAction.OpenRecommendedProduct -> action.productUrl
}
}

@ -166,4 +166,11 @@ sealed interface ReviewQualityCheckAction : Action {
* Triggered when the user reports a product is back in stock.
*/
object ReportProductBackInStock : TelemetryAction
/**
* Triggered when the user clicks on the recommended product.
*
* @property productUrl The product's link to open.
*/
data class OpenRecommendedProduct(val productUrl: String) : NavigationMiddlewareAction
}

@ -8,6 +8,7 @@ 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.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -18,6 +19,7 @@ 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.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.Icon
import androidx.compose.material.Text
@ -33,22 +35,26 @@ import androidx.compose.ui.layout.layout
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.Divider
import org.mozilla.fenix.compose.Image
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.ReviewQualityCheckState.OptedIn.ProductReviewState.AnalysisPresent.AnalysisStatus
import org.mozilla.fenix.shopping.store.ReviewQualityCheckState.RecommendedProductState
import org.mozilla.fenix.shopping.store.forCompactMode
import org.mozilla.fenix.theme.FirefoxTheme
import java.util.SortedMap
private val combinedParentHorizontalPadding = 32.dp
private val productRecommendationImageSize = 60.dp
/**
* UI for review quality check content displaying product analysis.
@ -64,6 +70,7 @@ private val combinedParentHorizontalPadding = 32.dp
* @param onFooterLinkClick Invoked when the user clicks on the footer link.
* @param onShowMoreRecentReviewsClicked Invoked when the user clicks to show more recent reviews.
* @param onExpandSettings Invoked when the user expands the settings card.
* @param onRecommendedProductClick Invoked when the user clicks on the product recommendation.
* @param modifier The modifier to be applied to the Composable.
*/
@Composable
@ -79,6 +86,7 @@ fun ProductAnalysis(
onFooterLinkClick: () -> Unit,
onShowMoreRecentReviewsClicked: () -> Unit,
onExpandSettings: () -> Unit,
onRecommendedProductClick: (String) -> Unit,
modifier: Modifier = Modifier,
) {
Column(
@ -129,6 +137,15 @@ fun ProductAnalysis(
onLearnMoreClick = onReviewGradeLearnMoreClick,
)
if (productAnalysis.recommendedProductState is RecommendedProductState.Product) {
ProductRecommendation(
product = productAnalysis.recommendedProductState,
onClick = {
onRecommendedProductClick(productAnalysis.recommendedProductState.productUrl)
},
)
}
ReviewQualityCheckSettingsCard(
productRecommendationsEnabled = productRecommendationsEnabled,
onProductRecommendationsEnabledStateChange = onProductRecommendationsEnabledStateChange,
@ -404,6 +421,68 @@ private enum class Highlight(
),
}
@Composable
private fun ProductRecommendation(
product: RecommendedProductState.Product,
onClick: () -> Unit,
) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
ReviewQualityCheckCard(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick),
) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
text = stringResource(R.string.review_quality_check_ad_title),
color = FirefoxTheme.colors.textPrimary,
style = FirefoxTheme.typography.headline8,
)
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
Image(
url = product.imageUrl,
modifier = Modifier.size(productRecommendationImageSize),
targetSize = productRecommendationImageSize,
)
Text(
text = product.name,
modifier = Modifier.weight(1.0f),
color = FirefoxTheme.colors.textAccent,
textDecoration = TextDecoration.Underline,
style = FirefoxTheme.typography.body2,
)
ReviewGradeCompact(grade = product.reviewGrade)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = product.formattedPrice,
color = FirefoxTheme.colors.textPrimary,
style = FirefoxTheme.typography.headline8,
)
StarRating(value = product.adjustedRating)
}
}
}
Text(
text = stringResource(
id = R.string.review_quality_check_ad_caption,
stringResource(id = R.string.shopping_product_name),
),
color = FirefoxTheme.colors.textSecondary,
style = FirefoxTheme.typography.body2,
)
}
}
private class ProductAnalysisPreviewModel(
val productRecommendationsEnabled: Boolean?,
val productAnalysis: AnalysisPresent,
@ -443,8 +522,7 @@ private class ProductAnalysisPreviewModel(
"Unbeatable deals",
),
),
recommendedProductState: ReviewQualityCheckState.RecommendedProductState =
ReviewQualityCheckState.RecommendedProductState.Initial,
recommendedProductState: RecommendedProductState = RecommendedProductState.Initial,
productVendor: ReviewQualityCheckState.ProductVendor = ReviewQualityCheckState.ProductVendor.AMAZON,
) : this(
productRecommendationsEnabled = productRecommendationsEnabled,
@ -483,6 +561,20 @@ private class ProductAnalysisPreviewModelParameterProvider :
),
),
),
ProductAnalysisPreviewModel(
productRecommendationsEnabled = true,
recommendedProductState = RecommendedProductState.Product(
name = "The best desk ever with a really really really long product name that " +
"forces the preview to wrap its text to at least 4 lines.",
productUrl = "www.mozilla.com",
imageUrl = "https://i.fakespot.io/b6vx27xf3rgwr1a597q6qd3rutp6",
formattedPrice = "$123.45",
reviewGrade = ReviewQualityCheckState.Grade.B,
adjustedRating = 4.23f,
isSponsored = true,
analysisUrl = "",
),
),
)
}
@ -510,6 +602,7 @@ private fun ProductAnalysisPreview(
onFooterLinkClick = {},
onShowMoreRecentReviewsClicked = {},
onExpandSettings = {},
onRecommendedProductClick = {},
)
}
}

@ -102,6 +102,10 @@ fun ReviewQualityCheckBottomSheet(
onShowMoreRecentReviewsClicked = {
store.dispatch(ReviewQualityCheckAction.ShowMoreRecentReviewsClicked)
},
onRecommendedProductClick = {
onRequestDismiss(BottomSheetDismissSource.LINK_OPENED)
store.dispatch(ReviewQualityCheckAction.OpenRecommendedProduct(it))
},
)
}
@ -129,6 +133,7 @@ private fun ProductReview(
onExpandSettings: () -> Unit,
onReviewGradeLearnMoreClick: () -> Unit,
onFooterLinkClick: () -> Unit,
onRecommendedProductClick: (String) -> Unit,
) {
Crossfade(
targetState = state.productReviewState,
@ -147,6 +152,7 @@ private fun ProductReview(
onExpandSettings = onExpandSettings,
onReviewGradeLearnMoreClick = onReviewGradeLearnMoreClick,
onFooterLinkClick = onFooterLinkClick,
onRecommendedProductClick = onRecommendedProductClick,
)
}

Loading…
Cancel
Save