Bug 1862399 - Keep review checker card expanded states in ReviewQualityCheckState

fenix/121.0
rahulsainani 8 months ago committed by mergify[bot]
parent 1ec2d118cf
commit 9c07a700fe

@ -5,6 +5,7 @@
package org.mozilla.fenix.shopping.middleware
import mozilla.components.lib.state.MiddlewareContext
import mozilla.components.lib.state.Store
import org.mozilla.fenix.GleanMetrics.Shopping
import org.mozilla.fenix.GleanMetrics.ShoppingSettings
import org.mozilla.fenix.shopping.store.ReviewQualityCheckAction
@ -24,7 +25,7 @@ class ReviewQualityCheckTelemetryMiddleware : ReviewQualityCheckMiddleware {
next(action)
when (action) {
is ReviewQualityCheckAction.TelemetryAction -> processAction(action)
is ReviewQualityCheckAction.TelemetryAction -> processAction(context.store, action)
else -> {
// no-op
}
@ -32,74 +33,81 @@ class ReviewQualityCheckTelemetryMiddleware : ReviewQualityCheckMiddleware {
}
private fun processAction(
store: Store<ReviewQualityCheckState, ReviewQualityCheckAction>,
action: ReviewQualityCheckAction.TelemetryAction,
) = when (action) {
is ReviewQualityCheckAction.OptIn -> {
Shopping.surfaceOptInAccepted.record()
ShoppingSettings.userHasOnboarded.set(true)
}
) {
when (action) {
is ReviewQualityCheckAction.OptIn -> {
Shopping.surfaceOptInAccepted.record()
ShoppingSettings.userHasOnboarded.set(true)
}
is ReviewQualityCheckAction.OptOut -> {
ShoppingSettings.componentOptedOut.set(true)
}
is ReviewQualityCheckAction.OptOut -> {
ShoppingSettings.componentOptedOut.set(true)
}
is ReviewQualityCheckAction.BottomSheetClosed -> {
Shopping.surfaceClosed.record(
Shopping.SurfaceClosedExtra(action.source.sourceName),
)
}
is ReviewQualityCheckAction.BottomSheetClosed -> {
Shopping.surfaceClosed.record(
Shopping.SurfaceClosedExtra(action.source.sourceName),
)
}
is ReviewQualityCheckAction.BottomSheetDisplayed -> {
Shopping.surfaceDisplayed.record(
Shopping.SurfaceDisplayedExtra(action.view.state),
)
}
is ReviewQualityCheckAction.BottomSheetDisplayed -> {
Shopping.surfaceDisplayed.record(
Shopping.SurfaceDisplayedExtra(action.view.state),
)
}
is ReviewQualityCheckAction.OpenExplainerLearnMoreLink -> {
Shopping.surfaceReviewQualityExplainerUrlClicked.record()
}
is ReviewQualityCheckAction.OpenExplainerLearnMoreLink -> {
Shopping.surfaceReviewQualityExplainerUrlClicked.record()
}
is ReviewQualityCheckAction.OpenOnboardingLearnMoreLink -> {
Shopping.surfaceLearnMoreClicked.record()
}
is ReviewQualityCheckAction.OpenOnboardingLearnMoreLink -> {
Shopping.surfaceLearnMoreClicked.record()
}
is ReviewQualityCheckAction.OpenOnboardingPrivacyPolicyLink -> {
Shopping.surfaceShowPrivacyPolicyClicked.record()
}
is ReviewQualityCheckAction.OpenOnboardingPrivacyPolicyLink -> {
Shopping.surfaceShowPrivacyPolicyClicked.record()
}
is ReviewQualityCheckAction.OpenOnboardingTermsLink -> {
Shopping.surfaceShowTermsClicked.record()
}
is ReviewQualityCheckAction.OpenOnboardingTermsLink -> {
Shopping.surfaceShowTermsClicked.record()
}
is ReviewQualityCheckAction.NotNowClicked -> {
Shopping.surfaceNotNowClicked.record()
}
is ReviewQualityCheckAction.NotNowClicked -> {
Shopping.surfaceNotNowClicked.record()
}
is ReviewQualityCheckAction.ShowMoreRecentReviewsClicked -> {
Shopping.surfaceShowMoreRecentReviewsClicked.record()
}
is ReviewQualityCheckAction.ShowMoreRecentReviewsClicked -> {
Shopping.surfaceShowMoreRecentReviewsClicked.record()
}
is ReviewQualityCheckAction.ExpandSettingsClicked -> {
Shopping.surfaceExpandSettings.record()
}
is ReviewQualityCheckAction.NoAnalysisDisplayed -> {
Shopping.surfaceNoReviewReliabilityAvailable.record()
}
is ReviewQualityCheckAction.ExpandCollapseSettings -> {
val state = store.state
if (state is ReviewQualityCheckState.OptedIn && state.isSettingsExpanded) {
Shopping.surfaceExpandSettings.record()
}
}
is ReviewQualityCheckAction.AnalyzeProduct -> {
Shopping.surfaceAnalyzeReviewsNoneAvailableClicked.record()
}
is ReviewQualityCheckAction.NoAnalysisDisplayed -> {
Shopping.surfaceNoReviewReliabilityAvailable.record()
}
is ReviewQualityCheckAction.ReanalyzeProduct -> {
Shopping.surfaceReanalyzeClicked.record()
}
is ReviewQualityCheckAction.AnalyzeProduct -> {
Shopping.surfaceAnalyzeReviewsNoneAvailableClicked.record()
}
is ReviewQualityCheckAction.ReportProductBackInStock -> {
Shopping.surfaceReactivatedButtonClicked.record()
}
is ReviewQualityCheckAction.ReanalyzeProduct -> {
Shopping.surfaceReanalyzeClicked.record()
}
is ReviewQualityCheckAction.ReportProductBackInStock -> {
Shopping.surfaceReactivatedButtonClicked.record()
}
is ReviewQualityCheckAction.OptOutCompleted -> {
Shopping.surfaceOnboardingDisplayed.record()
is ReviewQualityCheckAction.OptOutCompleted -> {
Shopping.surfaceOnboardingDisplayed.record()
}
}
}
}

@ -161,9 +161,14 @@ sealed interface ReviewQualityCheckAction : Action {
object ShowMoreRecentReviewsClicked : TelemetryAction
/**
* Triggered when the user expands the settings card.
* Triggered when the user expands or collapses the settings card.
*/
object ExpandSettingsClicked : TelemetryAction
object ExpandCollapseSettings : TelemetryAction, UpdateAction
/**
* Triggered when the user expands or collapses the info card.
*/
object ExpandCollapseInfo : UpdateAction
/**
* Triggered when the No analysis card is displayed to the user.

@ -43,11 +43,15 @@ sealed interface ReviewQualityCheckState : State {
* recommendations. True if product recommendations should be shown. Null indicates that product
* recommendations are disabled.
* @property productVendor The vendor of the product.
* @property isSettingsExpanded Whether or not the settings card is expanded.
* @property isInfoExpanded Whether or not the info card is expanded.
*/
data class OptedIn(
val productReviewState: ProductReviewState = ProductReviewState.Loading,
val productRecommendationsPreference: Boolean?,
val productVendor: ProductVendor,
val isSettingsExpanded: Boolean = false,
val isInfoExpanded: Boolean = false,
) : ReviewQualityCheckState {
/**

@ -11,12 +11,14 @@ import org.mozilla.fenix.shopping.store.ReviewQualityCheckState.OptedIn.ProductR
/**
* Store for review quality check feature.
*
* @param initialState The initial state of the store.
* @param middleware The list of middlewares to use.
*/
class ReviewQualityCheckStore(
initialState: ReviewQualityCheckState = ReviewQualityCheckState.Initial,
middleware: List<ReviewQualityCheckMiddleware>,
) : Store<ReviewQualityCheckState, ReviewQualityCheckAction>(
initialState = ReviewQualityCheckState.Initial,
initialState = initialState,
middleware = middleware,
reducer = ::reducer,
) {
@ -61,6 +63,18 @@ private fun mapStateForUpdateAction(
ReviewQualityCheckState.NotOptedIn()
}
ReviewQualityCheckAction.ExpandCollapseSettings -> {
state.mapIfOptedIn {
it.copy(isSettingsExpanded = !it.isSettingsExpanded)
}
}
ReviewQualityCheckAction.ExpandCollapseInfo -> {
state.mapIfOptedIn {
it.copy(isInfoExpanded = !it.isInfoExpanded)
}
}
ReviewQualityCheckAction.ToggleProductRecommendation -> {
if (state is ReviewQualityCheckState.OptedIn && state.productRecommendationsPreference != null) {
if (state.productReviewState is ProductReviewState.AnalysisPresent &&

@ -41,12 +41,15 @@ import org.mozilla.fenix.theme.FirefoxTheme
* @param isAnalyzing Whether or not the displayed product is being analyzed.
* @param productRecommendationsEnabled The current state of the product recommendations toggle.
* @param productVendor The vendor of the product.
* @param isSettingsExpanded Whether or not the settings card is expanded.
* @param isInfoExpanded Whether or not the info card is expanded.
* @param onAnalyzeClick Invoked when the user clicks on the check review button.
* @param onReviewGradeLearnMoreClick Invoked when the user clicks to learn more about review grades.
* @param onOptOutClick Invoked when the user opts out of the review quality check feature.
* @param onProductRecommendationsEnabledStateChange Invoked when the user changes the product
* recommendations toggle state.
* @param onExpandSettings Invoked when the user expands the settings card.
* @param onSettingsExpandToggleClick Invoked when the user expands or collapses the settings card.
* @param onInfoExpandToggleClick Invoked when the user expands or collapses the info card.
* @param modifier Modifier to be applied to the composable.
*/
@Suppress("LongParameterList")
@ -55,11 +58,14 @@ fun NoAnalysis(
isAnalyzing: Boolean,
productRecommendationsEnabled: Boolean?,
productVendor: ReviewQualityCheckState.ProductVendor,
isSettingsExpanded: Boolean,
isInfoExpanded: Boolean,
onAnalyzeClick: () -> Unit,
onReviewGradeLearnMoreClick: () -> Unit,
onOptOutClick: () -> Unit,
onProductRecommendationsEnabledStateChange: (Boolean) -> Unit,
onExpandSettings: () -> Unit,
onSettingsExpandToggleClick: () -> Unit,
onInfoExpandToggleClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
@ -70,14 +76,17 @@ fun NoAnalysis(
ReviewQualityInfoCard(
productVendor = productVendor,
isExpanded = isInfoExpanded,
onLearnMoreClick = onReviewGradeLearnMoreClick,
onExpandToggleClick = onInfoExpandToggleClick,
)
ReviewQualityCheckSettingsCard(
productRecommendationsEnabled = productRecommendationsEnabled,
isExpanded = isSettingsExpanded,
onProductRecommendationsEnabledStateChange = onProductRecommendationsEnabledStateChange,
onTurnOffReviewQualityCheckClick = onOptOutClick,
onExpandSettings = onExpandSettings,
onExpandToggleClick = onSettingsExpandToggleClick,
modifier = Modifier.fillMaxWidth(),
)
}
@ -169,8 +178,6 @@ private fun ReviewQualityNoAnalysisCard(
@Composable
@LightDarkPreview
private fun NoAnalysisPreview() {
var isAnalyzing by remember { mutableStateOf(false) }
FirefoxTheme {
Box(
modifier = Modifier
@ -178,15 +185,23 @@ private fun NoAnalysisPreview() {
.background(color = FirefoxTheme.colors.layer1)
.padding(all = 16.dp),
) {
var isAnalyzing by remember { mutableStateOf(false) }
var productRecommendationsEnabled by remember { mutableStateOf(false) }
var isSettingsExpanded by remember { mutableStateOf(false) }
var isInfoExpanded by remember { mutableStateOf(false) }
NoAnalysis(
isAnalyzing = isAnalyzing,
productVendor = ReviewQualityCheckState.ProductVendor.AMAZON,
onAnalyzeClick = { isAnalyzing = !isAnalyzing },
productRecommendationsEnabled = false,
productVendor = ReviewQualityCheckState.ProductVendor.AMAZON,
productRecommendationsEnabled = productRecommendationsEnabled,
isSettingsExpanded = isSettingsExpanded,
isInfoExpanded = isInfoExpanded,
onReviewGradeLearnMoreClick = {},
onOptOutClick = {},
onProductRecommendationsEnabledStateChange = {},
onExpandSettings = {},
onProductRecommendationsEnabledStateChange = { productRecommendationsEnabled = it },
onSettingsExpandToggleClick = { isSettingsExpanded = !isSettingsExpanded },
onInfoExpandToggleClick = { isInfoExpanded = !isInfoExpanded },
modifier = Modifier.fillMaxWidth(),
)
}

@ -62,6 +62,8 @@ private val productRecommendationImageSize = 60.dp
* @param productRecommendationsEnabled The current state of the product recommendations toggle.
* @param productAnalysis The product analysis to display.
* @param productVendor The vendor of the product.
* @param isSettingsExpanded Whether or not the settings card is expanded.
* @param isInfoExpanded Whether or not the info card is expanded.
* @param onOptOutClick Invoked when the user opts out of the review quality check feature.
* @param onReanalyzeClick Invoked when the user clicks to re-analyze a product.
* @param onProductRecommendationsEnabledStateChange Invoked when the user changes the product
@ -69,7 +71,8 @@ private val productRecommendationImageSize = 60.dp
* @param onReviewGradeLearnMoreClick Invoked when the user clicks to learn more about review grades.
* @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 onSettingsExpandToggleClick Invoked when the user expands or collapses the settings card.
* @param onInfoExpandToggleClick Invoked when the user expands or collapses the info card.
* @param onRecommendedProductClick Invoked when the user clicks on the product recommendation.
* @param modifier The modifier to be applied to the Composable.
*/
@ -79,13 +82,16 @@ fun ProductAnalysis(
productRecommendationsEnabled: Boolean?,
productAnalysis: AnalysisPresent,
productVendor: ReviewQualityCheckState.ProductVendor,
isSettingsExpanded: Boolean,
isInfoExpanded: Boolean,
onOptOutClick: () -> Unit,
onReanalyzeClick: () -> Unit,
onProductRecommendationsEnabledStateChange: (Boolean) -> Unit,
onReviewGradeLearnMoreClick: () -> Unit,
onFooterLinkClick: () -> Unit,
onShowMoreRecentReviewsClicked: () -> Unit,
onExpandSettings: () -> Unit,
onSettingsExpandToggleClick: () -> Unit,
onInfoExpandToggleClick: () -> Unit,
onRecommendedProductClick: (String) -> Unit,
modifier: Modifier = Modifier,
) {
@ -133,7 +139,9 @@ fun ProductAnalysis(
ReviewQualityInfoCard(
productVendor = productVendor,
isExpanded = isInfoExpanded,
modifier = Modifier.fillMaxWidth(),
onExpandToggleClick = onInfoExpandToggleClick,
onLearnMoreClick = onReviewGradeLearnMoreClick,
)
@ -148,9 +156,10 @@ fun ProductAnalysis(
ReviewQualityCheckSettingsCard(
productRecommendationsEnabled = productRecommendationsEnabled,
isExpanded = isSettingsExpanded,
onProductRecommendationsEnabledStateChange = onProductRecommendationsEnabledStateChange,
onTurnOffReviewQualityCheckClick = onOptOutClick,
onExpandSettings = onExpandSettings,
onExpandToggleClick = onSettingsExpandToggleClick,
modifier = Modifier.fillMaxWidth(),
)
@ -589,11 +598,15 @@ private fun ProductAnalysisPreview(
onRequestDismiss = {},
) {
var productRecommendationsEnabled by remember { mutableStateOf(model.productRecommendationsEnabled) }
var isSettingsExpanded by remember { mutableStateOf(false) }
var isInfoExpanded by remember { mutableStateOf(false) }
ProductAnalysis(
productRecommendationsEnabled = productRecommendationsEnabled,
productAnalysis = model.productAnalysis,
productVendor = model.productVendor,
isSettingsExpanded = isSettingsExpanded,
isInfoExpanded = isInfoExpanded,
onOptOutClick = {},
onReanalyzeClick = {},
onProductRecommendationsEnabledStateChange = {
@ -602,7 +615,8 @@ private fun ProductAnalysisPreview(
onReviewGradeLearnMoreClick = {},
onFooterLinkClick = {},
onShowMoreRecentReviewsClicked = {},
onExpandSettings = {},
onSettingsExpandToggleClick = { isSettingsExpanded = !isSettingsExpanded },
onInfoExpandToggleClick = { isInfoExpanded = !isInfoExpanded },
onRecommendedProductClick = {},
)
}

@ -12,6 +12,10 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
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.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -27,12 +31,15 @@ import org.mozilla.fenix.theme.FirefoxTheme
* @param error The error state to display.
* @param productRecommendationsEnabled The current state of the product recommendations toggle.
* @param productVendor The vendor of the product.
* @param isSettingsExpanded Whether or not the settings card is expanded.
* @param isInfoExpanded Whether or not the info card is expanded.
* @param onReviewGradeLearnMoreClick Invoked when the user clicks to learn more about review grades.
* @param onOptOutClick Invoked when the user opts out of the review quality check feature.
* @param onProductRecommendationsEnabledStateChange Invoked when the user changes the product
* recommendations toggle state.
* @param onFooterLinkClick Invoked when the user clicks on the footer link.
* @param onExpandSettings Invoked when the user expands the settings card.
* @param onSettingsExpandToggleClick Invoked when the user expands or collapses the settings card.
* @param onInfoExpandToggleClick Invoked when the user expands or collapses the info card.
* @param modifier Modifier to apply to the layout.
*/
@Composable
@ -41,11 +48,14 @@ fun ProductAnalysisError(
error: ProductReviewState.Error,
productRecommendationsEnabled: Boolean?,
productVendor: ReviewQualityCheckState.ProductVendor,
isSettingsExpanded: Boolean,
isInfoExpanded: Boolean,
onReviewGradeLearnMoreClick: () -> Unit,
onOptOutClick: () -> Unit,
onProductRecommendationsEnabledStateChange: (Boolean) -> Unit,
onFooterLinkClick: () -> Unit,
onExpandSettings: () -> Unit,
onSettingsExpandToggleClick: () -> Unit,
onInfoExpandToggleClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
@ -99,14 +109,17 @@ fun ProductAnalysisError(
ReviewQualityInfoCard(
productVendor = productVendor,
isExpanded = isInfoExpanded,
onLearnMoreClick = onReviewGradeLearnMoreClick,
onExpandToggleClick = onInfoExpandToggleClick,
)
ReviewQualityCheckSettingsCard(
productRecommendationsEnabled = productRecommendationsEnabled,
isExpanded = isSettingsExpanded,
onProductRecommendationsEnabledStateChange = onProductRecommendationsEnabledStateChange,
onTurnOffReviewQualityCheckClick = onOptOutClick,
onExpandSettings = onExpandSettings,
onExpandToggleClick = onSettingsExpandToggleClick,
modifier = Modifier.fillMaxWidth(),
)
@ -126,15 +139,22 @@ private fun ProductAnalysisErrorPreview() {
.background(color = FirefoxTheme.colors.layer1)
.padding(all = 16.dp),
) {
var productRecommendationsEnabled by remember { mutableStateOf(false) }
var isSettingsExpanded by remember { mutableStateOf(false) }
var isInfoExpanded by remember { mutableStateOf(false) }
ProductAnalysisError(
error = ProductReviewState.Error.NetworkError,
productRecommendationsEnabled = true,
productRecommendationsEnabled = productRecommendationsEnabled,
productVendor = ReviewQualityCheckState.ProductVendor.AMAZON,
isSettingsExpanded = isSettingsExpanded,
isInfoExpanded = isInfoExpanded,
onReviewGradeLearnMoreClick = {},
onOptOutClick = {},
onProductRecommendationsEnabledStateChange = {},
onProductRecommendationsEnabledStateChange = { productRecommendationsEnabled = it },
onFooterLinkClick = {},
onExpandSettings = {},
onSettingsExpandToggleClick = { isSettingsExpanded = !isSettingsExpanded },
onInfoExpandToggleClick = { isInfoExpanded = !isInfoExpanded },
modifier = Modifier.fillMaxWidth(),
)
}

@ -93,8 +93,11 @@ fun ReviewQualityCheckBottomSheet(
onRequestDismiss(BottomSheetDismissSource.LINK_OPENED)
store.dispatch(ReviewQualityCheckAction.OpenPoweredByLink)
},
onExpandSettings = {
store.dispatch(ReviewQualityCheckAction.ExpandSettingsClicked)
onSettingsExpandToggleClick = {
store.dispatch(ReviewQualityCheckAction.ExpandCollapseSettings)
},
onInfoExpandToggleClick = {
store.dispatch(ReviewQualityCheckAction.ExpandCollapseInfo)
},
onNoAnalysisPresent = {
store.dispatch(ReviewQualityCheckAction.NoAnalysisDisplayed)
@ -130,7 +133,8 @@ private fun ProductReview(
onProductRecommendationsEnabledStateChange: (Boolean) -> Unit,
onShowMoreRecentReviewsClicked: () -> Unit,
onNoAnalysisPresent: () -> Unit,
onExpandSettings: () -> Unit,
onSettingsExpandToggleClick: () -> Unit,
onInfoExpandToggleClick: () -> Unit,
onReviewGradeLearnMoreClick: () -> Unit,
onFooterLinkClick: () -> Unit,
onRecommendedProductClick: (String) -> Unit,
@ -145,11 +149,14 @@ private fun ProductReview(
productRecommendationsEnabled = state.productRecommendationsPreference,
productAnalysis = productReviewState,
productVendor = state.productVendor,
isSettingsExpanded = state.isSettingsExpanded,
isInfoExpanded = state.isInfoExpanded,
onOptOutClick = onOptOutClick,
onReanalyzeClick = onReanalyzeClick,
onProductRecommendationsEnabledStateChange = onProductRecommendationsEnabledStateChange,
onShowMoreRecentReviewsClicked = onShowMoreRecentReviewsClicked,
onExpandSettings = onExpandSettings,
onSettingsExpandToggleClick = onSettingsExpandToggleClick,
onInfoExpandToggleClick = onInfoExpandToggleClick,
onReviewGradeLearnMoreClick = onReviewGradeLearnMoreClick,
onFooterLinkClick = onFooterLinkClick,
onRecommendedProductClick = onRecommendedProductClick,
@ -161,11 +168,14 @@ private fun ProductReview(
error = productReviewState,
productRecommendationsEnabled = state.productRecommendationsPreference,
productVendor = state.productVendor,
isSettingsExpanded = state.isSettingsExpanded,
isInfoExpanded = state.isInfoExpanded,
onReviewGradeLearnMoreClick = onReviewGradeLearnMoreClick,
onOptOutClick = onOptOutClick,
onProductRecommendationsEnabledStateChange = onProductRecommendationsEnabledStateChange,
onFooterLinkClick = onFooterLinkClick,
onExpandSettings = onExpandSettings,
onSettingsExpandToggleClick = onSettingsExpandToggleClick,
onInfoExpandToggleClick = onInfoExpandToggleClick,
modifier = Modifier.fillMaxWidth(),
)
}
@ -181,13 +191,16 @@ private fun ProductReview(
NoAnalysis(
isAnalyzing = productReviewState.isReanalyzing,
onAnalyzeClick = onAnalyzeClick,
productRecommendationsEnabled = state.productRecommendationsPreference,
productVendor = state.productVendor,
onAnalyzeClick = onAnalyzeClick,
isSettingsExpanded = state.isSettingsExpanded,
isInfoExpanded = state.isInfoExpanded,
onReviewGradeLearnMoreClick = onReviewGradeLearnMoreClick,
onOptOutClick = onOptOutClick,
onProductRecommendationsEnabledStateChange = onProductRecommendationsEnabledStateChange,
onExpandSettings = onExpandSettings,
onSettingsExpandToggleClick = onSettingsExpandToggleClick,
onInfoExpandToggleClick = onInfoExpandToggleClick,
modifier = Modifier.fillMaxWidth(),
)
}

@ -44,23 +44,23 @@ private val defaultCardContentPadding = 16.dp
* A card container for review quality check UI that can be expanded and collapsed.
*
* @param title The title of the card.
* @param isExpanded Whether or not the card is expanded.
* @param modifier Modifier to be applied to the card.
* @param onExpandToggleClick Callback invoked when card is collapsed or expanded.
* @param onExpandToggleClick Invoked when the card is expanded or collapsed.
* @param content The content of the card.
*/
@Composable
fun ReviewQualityCheckExpandableCard(
title: String,
isExpanded: Boolean,
modifier: Modifier = Modifier,
onExpandToggleClick: (isExpanded: Boolean) -> Unit = {},
onExpandToggleClick: () -> Unit,
content: @Composable () -> Unit,
) {
ReviewQualityCheckCard(
modifier = modifier,
contentPadding = PaddingValues(0.dp),
) {
var isExpanded by remember { mutableStateOf(false) }
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
@ -71,10 +71,7 @@ fun ReviewQualityCheckExpandableCard(
} else {
stringResource(R.string.a11y_action_label_expand)
},
onClick = {
isExpanded = isExpanded.not()
onExpandToggleClick(isExpanded)
},
onClick = onExpandToggleClick,
)
.padding(defaultCardContentPadding),
verticalAlignment = Alignment.CenterVertically,
@ -152,6 +149,8 @@ fun ReviewQualityCheckCard(
private fun ReviewQualityCheckCardPreview() {
FirefoxTheme {
Column(modifier = Modifier.padding(16.dp)) {
var isExpanded by remember { mutableStateOf(true) }
ReviewQualityCheckCard(
modifier = Modifier.fillMaxWidth(),
) {
@ -167,6 +166,8 @@ private fun ReviewQualityCheckCardPreview() {
ReviewQualityCheckExpandableCard(
title = "Review Quality Check Expandable Card",
modifier = Modifier.fillMaxWidth(),
isExpanded = isExpanded,
onExpandToggleClick = { isExpanded = !isExpanded },
) {
Text(
text = "content",

@ -12,6 +12,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
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.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -26,28 +30,27 @@ import org.mozilla.fenix.theme.FirefoxTheme
* the entire review quality check feature.
*
* @param productRecommendationsEnabled The current state of the product recommendations toggle.
* @param isExpanded Whether or not the settings card is expanded.
* @param onProductRecommendationsEnabledStateChange Invoked when the user changes the product
* recommendations toggle state.
* @param onTurnOffReviewQualityCheckClick Invoked when the user opts out of the review quality check feature.
* @param onExpandSettings Invoked when the user expands the settings card.
* @param onExpandToggleClick Invoked when the user expands or collapses the settings card.
* @param modifier Modifier to apply to the layout.
*/
@Composable
fun ReviewQualityCheckSettingsCard(
productRecommendationsEnabled: Boolean?,
isExpanded: Boolean,
onProductRecommendationsEnabledStateChange: (Boolean) -> Unit,
onTurnOffReviewQualityCheckClick: () -> Unit,
onExpandSettings: () -> Unit,
onExpandToggleClick: () -> Unit,
modifier: Modifier = Modifier,
) {
ReviewQualityCheckExpandableCard(
modifier = modifier,
title = stringResource(R.string.review_quality_check_settings_title),
onExpandToggleClick = { isExpanded ->
if (isExpanded) {
onExpandSettings()
}
},
isExpanded = isExpanded,
onExpandToggleClick = onExpandToggleClick,
) {
SettingsContent(
productRecommendationsEnabled = productRecommendationsEnabled,
@ -95,31 +98,15 @@ private fun ReviewQualityCheckSettingsCardPreview() {
.background(color = FirefoxTheme.colors.layer1)
.padding(all = 16.dp),
) {
ReviewQualityCheckSettingsCard(
productRecommendationsEnabled = true,
onProductRecommendationsEnabledStateChange = {},
onTurnOffReviewQualityCheckClick = {},
onExpandSettings = {},
modifier = Modifier.fillMaxWidth(),
)
}
}
}
var isSettingsExpanded by remember { mutableStateOf(true) }
var productRecommendationsEnabled by remember { mutableStateOf(true) }
@LightDarkPreview
@Composable
private fun SettingsContentPreview() {
FirefoxTheme {
Box(
modifier = Modifier
.fillMaxWidth()
.background(color = FirefoxTheme.colors.layer1)
.padding(all = 16.dp),
) {
SettingsContent(
productRecommendationsEnabled = true,
onProductRecommendationsEnabledStateChange = {},
ReviewQualityCheckSettingsCard(
productRecommendationsEnabled = productRecommendationsEnabled,
onProductRecommendationsEnabledStateChange = { productRecommendationsEnabled = it },
onTurnOffReviewQualityCheckClick = {},
isExpanded = isSettingsExpanded,
onExpandToggleClick = { isSettingsExpanded = !isSettingsExpanded },
modifier = Modifier.fillMaxWidth(),
)
}

@ -15,7 +15,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
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.res.stringResource
@ -35,18 +38,24 @@ import org.mozilla.fenix.theme.FirefoxTheme
* Info card UI containing an explanation of the review quality.
*
* @param productVendor The vendor of the product.
* @param isExpanded Whether or not the card is expanded.
* @param modifier Modifier to apply to the layout.
* @param onExpandToggleClick Invoked when the user expands or collapses the card.
* @param onLearnMoreClick Invoked when the user clicks to learn more about review grades.
*/
@Composable
fun ReviewQualityInfoCard(
productVendor: ReviewQualityCheckState.ProductVendor,
isExpanded: Boolean,
modifier: Modifier = Modifier,
onExpandToggleClick: () -> Unit,
onLearnMoreClick: () -> Unit,
) {
ReviewQualityCheckExpandableCard(
title = stringResource(id = R.string.review_quality_check_explanation_title),
modifier = modifier,
isExpanded = isExpanded,
onExpandToggleClick = onExpandToggleClick,
) {
ReviewQualityInfo(
productVendor = productVendor,
@ -189,29 +198,14 @@ private fun ReviewQualityInfoCardPreview() {
.background(color = FirefoxTheme.colors.layer1)
.padding(all = 16.dp),
) {
ReviewQualityInfoCard(
productVendor = ReviewQualityCheckState.ProductVendor.AMAZON,
modifier = Modifier.fillMaxWidth(),
onLearnMoreClick = {},
)
}
}
}
var isInfoExpanded by remember { mutableStateOf(true) }
@Composable
@LightDarkPreview
private fun ReviewQualityInfoPreview() {
FirefoxTheme {
Box(
modifier = Modifier
.fillMaxWidth()
.background(color = FirefoxTheme.colors.layer1)
.padding(all = 16.dp),
) {
ReviewQualityInfo(
ReviewQualityInfoCard(
productVendor = ReviewQualityCheckState.ProductVendor.AMAZON,
isExpanded = isInfoExpanded,
modifier = Modifier.fillMaxWidth(),
onLearnMoreClick = {},
onExpandToggleClick = { isInfoExpanded = !isInfoExpanded },
)
}
}

@ -21,7 +21,7 @@ class ReviewQualityCheckBottomSheetStateFeatureTest {
@Test
fun `WHEN store state changes to not opted in from any other state THEN callback is invoked with half state`() {
val store = ReviewQualityCheckStore(emptyList())
val store = ReviewQualityCheckStore(middleware = emptyList())
var updatedState: BottomSheetViewState? = null
val tested = ReviewQualityCheckBottomSheetStateFeature(
store = store,
@ -44,7 +44,7 @@ class ReviewQualityCheckBottomSheetStateFeatureTest {
@Test
fun `WHEN store state changes to not opted in from initial state THEN callback is invoked with full state`() {
val store = ReviewQualityCheckStore(emptyList())
val store = ReviewQualityCheckStore(middleware = emptyList())
var updatedState: BottomSheetViewState? = null
val tested = ReviewQualityCheckBottomSheetStateFeature(
store = store,

@ -10,6 +10,7 @@ import mozilla.components.support.test.libstate.ext.waitUntilIdle
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -19,6 +20,7 @@ import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.shopping.store.BottomSheetDismissSource
import org.mozilla.fenix.shopping.store.BottomSheetViewState
import org.mozilla.fenix.shopping.store.ReviewQualityCheckAction
import org.mozilla.fenix.shopping.store.ReviewQualityCheckState
import org.mozilla.fenix.shopping.store.ReviewQualityCheckStore
@RunWith(FenixRobolectricTestRunner::class)
@ -119,12 +121,42 @@ class ReviewQualityCheckTelemetryMiddlewareTest {
@Test
fun `WHEN the expand button from the settings card is clicked THEN the settings expand event is recorded`() {
store.dispatch(ReviewQualityCheckAction.ExpandSettingsClicked).joinBlocking()
store.waitUntilIdle()
val tested = ReviewQualityCheckStore(
initialState = ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = true,
productVendor = ReviewQualityCheckState.ProductVendor.AMAZON,
isSettingsExpanded = false,
),
middleware = listOf(
ReviewQualityCheckTelemetryMiddleware(),
),
)
tested.waitUntilIdle()
tested.dispatch(ReviewQualityCheckAction.ExpandCollapseSettings).joinBlocking()
tested.waitUntilIdle()
assertNotNull(Shopping.surfaceExpandSettings.testGetValue())
}
@Test
fun `WHEN the collapse button from the settings card is clicked THEN the settings expand event is not recorded`() {
val tested = ReviewQualityCheckStore(
initialState = ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = true,
productVendor = ReviewQualityCheckState.ProductVendor.AMAZON,
isSettingsExpanded = true,
),
middleware = listOf(
ReviewQualityCheckTelemetryMiddleware(),
),
)
tested.waitUntilIdle()
tested.dispatch(ReviewQualityCheckAction.ExpandCollapseSettings).joinBlocking()
tested.waitUntilIdle()
assertNull(Shopping.surfaceExpandSettings.testGetValue())
}
@Test
fun `WHEN no analysis is present THEN the no analysis event is recorded`() {
store.dispatch(ReviewQualityCheckAction.NoAnalysisDisplayed).joinBlocking()

@ -207,6 +207,106 @@ class ReviewQualityCheckStoreTest {
assertEquals(expected, tested.state)
}
@Test
fun `GIVEN the user has opted in the feature WHEN the user expands settings THEN state should reflect that`() =
runTest {
val tested = ReviewQualityCheckStore(
initialState = ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = null,
productVendor = ProductVendor.BEST_BUY,
isSettingsExpanded = false,
),
middleware = emptyList(),
)
tested.waitUntilIdle()
dispatcher.scheduler.advanceUntilIdle()
tested.dispatch(ReviewQualityCheckAction.ExpandCollapseSettings).joinBlocking()
tested.waitUntilIdle()
dispatcher.scheduler.advanceUntilIdle()
val expected = ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = null,
productVendor = ProductVendor.BEST_BUY,
isSettingsExpanded = true,
)
assertEquals(expected, tested.state)
}
@Test
fun `GIVEN the user has opted in the feature WHEN the user collapses settings THEN state should reflect that`() =
runTest {
val tested = ReviewQualityCheckStore(
initialState = ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = null,
productVendor = ProductVendor.BEST_BUY,
isSettingsExpanded = true,
),
middleware = emptyList(),
)
tested.waitUntilIdle()
dispatcher.scheduler.advanceUntilIdle()
tested.dispatch(ReviewQualityCheckAction.ExpandCollapseSettings).joinBlocking()
tested.waitUntilIdle()
dispatcher.scheduler.advanceUntilIdle()
val expected = ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = null,
productVendor = ProductVendor.BEST_BUY,
isSettingsExpanded = false,
)
assertEquals(expected, tested.state)
}
@Test
fun `GIVEN the user has opted in the feature WHEN the user expands info card THEN state should reflect that`() =
runTest {
val tested = ReviewQualityCheckStore(
initialState = ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = null,
productVendor = ProductVendor.BEST_BUY,
isInfoExpanded = false,
),
middleware = emptyList(),
)
tested.waitUntilIdle()
dispatcher.scheduler.advanceUntilIdle()
tested.dispatch(ReviewQualityCheckAction.ExpandCollapseInfo).joinBlocking()
tested.waitUntilIdle()
dispatcher.scheduler.advanceUntilIdle()
val expected = ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = null,
productVendor = ProductVendor.BEST_BUY,
isInfoExpanded = true,
)
assertEquals(expected, tested.state)
}
@Test
fun `GIVEN the user has opted in the feature WHEN the user collapses info card THEN state should reflect that`() =
runTest {
val tested = ReviewQualityCheckStore(
initialState = ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = null,
productVendor = ProductVendor.BEST_BUY,
isInfoExpanded = true,
),
middleware = emptyList(),
)
tested.waitUntilIdle()
dispatcher.scheduler.advanceUntilIdle()
tested.dispatch(ReviewQualityCheckAction.ExpandCollapseInfo).joinBlocking()
tested.waitUntilIdle()
dispatcher.scheduler.advanceUntilIdle()
val expected = ReviewQualityCheckState.OptedIn(
productRecommendationsPreference = null,
productVendor = ProductVendor.BEST_BUY,
isInfoExpanded = false,
)
assertEquals(expected, tested.state)
}
@Test
fun `GIVEN the user has opted in the feature WHEN a product analysis is fetched successfully THEN state should reflect that`() =
runTest {

Loading…
Cancel
Save