From c046178df4f8bb792fe6e8d9253e2cd1483f775f Mon Sep 17 00:00:00 2001 From: Artem Chepurnoy Date: Mon, 2 Sep 2024 11:11:02 +0300 Subject: [PATCH] improvement(Android): Small redesign for Products to solve Play store issues --- .../component/SettingSubscriptions.kt | 266 ++++++++---------- 1 file changed, 121 insertions(+), 145 deletions(-) diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/component/SettingSubscriptions.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/component/SettingSubscriptions.kt index cae05d08..191805ee 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/component/SettingSubscriptions.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/component/SettingSubscriptions.kt @@ -1,14 +1,13 @@ package com.artemchep.keyguard.feature.home.settings.component -import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -18,7 +17,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Star import androidx.compose.material3.Icon @@ -26,7 +24,6 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue @@ -53,18 +50,12 @@ import com.artemchep.keyguard.feature.onboarding.onboardingItemsPremium import com.artemchep.keyguard.platform.LocalLeContext import com.artemchep.keyguard.res.Res import com.artemchep.keyguard.res.* -import com.artemchep.keyguard.ui.Ah import com.artemchep.keyguard.ui.DefaultEmphasisAlpha -import com.artemchep.keyguard.ui.ExpandedIfNotEmpty -import com.artemchep.keyguard.ui.FlatItemLayout -import com.artemchep.keyguard.ui.FlatItemTextContent import com.artemchep.keyguard.ui.FlatSimpleNote import com.artemchep.keyguard.ui.FlatTextFieldBadge import com.artemchep.keyguard.ui.GridLayout import com.artemchep.keyguard.ui.MediumEmphasisAlpha import com.artemchep.keyguard.ui.SimpleNote -import com.artemchep.keyguard.ui.icons.ChevronIcon -import com.artemchep.keyguard.ui.shimmer.shimmer import com.artemchep.keyguard.ui.skeleton.SkeletonText import com.artemchep.keyguard.ui.theme.Dimens import com.artemchep.keyguard.ui.theme.combineAlpha @@ -164,13 +155,7 @@ private fun SettingSubscriptions( Section(text = stringResource(Res.string.pref_item_premium_membership_section_subscriptions_title)) loadableSubscriptions.fold( ifLoading = { - GridLayout( - modifier = Modifier - .padding(horizontal = 8.dp), - columns = 2, - mainAxisSpacing = 8.dp, - crossAxisSpacing = 8.dp, - ) { + SettingGroupLayout { repeat(SubscriptionsCountDefault) { SettingSubscriptionSkeletonItem( modifier = Modifier, @@ -186,13 +171,7 @@ private fun SettingSubscriptions( ) return@fold } - GridLayout( - modifier = Modifier - .padding(horizontal = 8.dp), - columns = 2, - mainAxisSpacing = 8.dp, - crossAxisSpacing = 8.dp, - ) { + SettingGroupLayout { subscriptions.forEach { subscription -> SettingSubscriptionItem( modifier = Modifier, @@ -215,8 +194,10 @@ private fun SettingSubscriptions( Section(text = stringResource(Res.string.pref_item_premium_membership_section_products_title)) loadableProducts.fold( ifLoading = { - repeat(ProductsCountDefault) { - SettingSubscriptionSkeletonItem() + SettingGroupLayout { + repeat(ProductsCountDefault) { + SettingSubscriptionSkeletonItem() + } } }, ifOk = { products -> @@ -227,10 +208,12 @@ private fun SettingSubscriptions( ) return@fold } - products.forEach { product -> - SettingProductItem( - product = product, - ) + SettingGroupLayout { + products.forEach { product -> + SettingProductItem( + product = product, + ) + } } }, ) @@ -241,28 +224,27 @@ private fun SettingSubscriptions( private fun SettingSubscriptionSkeletonItem( modifier: Modifier = Modifier, ) { - SettingSubscriptionContentItem( - modifier = modifier - .shimmer(), + SettingItemLayout( + modifier = modifier, isActive = false, - ) { - Column( - modifier = Modifier - .padding(8.dp), - ) { + onClick = { + // Do nothing + }, + title = { SkeletonText( modifier = Modifier .fillMaxWidth(0.45f), style = MaterialTheme.typography.titleSmall, ) - Spacer(modifier = Modifier.height(8.dp)) + }, + price = { SkeletonText( modifier = Modifier .fillMaxWidth(0.45f), style = MaterialTheme.typography.titleMedium, ) - } - } + }, + ) } @Composable @@ -271,44 +253,23 @@ private fun SettingSubscriptionItem( subscription: Subscription, ) { val context by rememberUpdatedState(LocalLeContext) - SettingSubscriptionContentItem( + val status = subscription.status + SettingItemLayout( modifier = modifier, - isActive = subscription.status is Subscription.Status.Active, - ) { - Column( - modifier = Modifier - .clickable(role = Role.Button) { - subscription.purchase(context) - } - .padding(8.dp), - ) { - val localEmphasis = DefaultEmphasisAlpha - val localTextStyle = TextStyle( - color = LocalContentColor.current - .combineAlpha(localEmphasis), - ) - - CompositionLocalProvider( - LocalTextStyle provides MaterialTheme.typography.titleSmall - .merge(localTextStyle), - ) { - Text( - subscription.title, - color = LocalContentColor.current - .combineAlpha(MediumEmphasisAlpha), - ) - } - Spacer(modifier = Modifier.height(8.dp)) - CompositionLocalProvider( - LocalTextStyle provides MaterialTheme.typography.titleMedium - .merge(localTextStyle), - ) { - val text = "${subscription.price} / ${subscription.periodFormatted}" - Text(text) - } - Spacer(modifier = Modifier.height(8.dp)) - val status = subscription.status - if (status is Subscription.Status.Inactive && status.trialPeriodFormatted != null) { + isActive = status is Subscription.Status.Active, + onClick = { + subscription.purchase(context) + }, + title = { + Text(subscription.title) + }, + price = { + val text = "${subscription.price} / ${subscription.periodFormatted}" + Text(text) + }, + content = if (status is Subscription.Status.Inactive && status.trialPeriodFormatted != null) { + // composable + { FlatTextFieldBadge( type = TextFieldModel2.Vl.Type.INFO, text = stringResource( @@ -317,15 +278,58 @@ private fun SettingSubscriptionItem( ), ) } - } + } else { + null + }, + ) +} + +@Composable +private fun SettingProductItem( + modifier: Modifier = Modifier, + product: Product, +) { + val context by rememberUpdatedState(LocalLeContext) + val status = product.status + SettingItemLayout( + modifier = modifier, + isActive = status is Product.Status.Active, + onClick = { + product.purchase(context) + }, + title = { + Text(product.title) + }, + price = { + Text(product.price) + }, + ) +} + +@Composable +private fun SettingGroupLayout( + modifier: Modifier = Modifier, + content: @Composable () -> Unit, +) { + GridLayout( + modifier = modifier + .padding(horizontal = 8.dp), + columns = 2, + mainAxisSpacing = 8.dp, + crossAxisSpacing = 8.dp, + ) { + content() } } @Composable -private fun SettingSubscriptionContentItem( +private fun SettingItemLayout( modifier: Modifier = Modifier, isActive: Boolean, - content: @Composable BoxScope.() -> Unit, + onClick: () -> Unit, + title: @Composable ColumnScope.() -> Unit, + price: @Composable ColumnScope.() -> Unit, + content: (@Composable ColumnScope.() -> Unit)? = null, ) { val backgroundModifier = run { val tintColor = MaterialTheme.colorScheme @@ -357,69 +361,41 @@ private fun SettingSubscriptionContentItem( contentDescription = null, ) } - content() + val updatedOnClick by rememberUpdatedState(onClick) + Column( + modifier = Modifier + .clickable(role = Role.Button) { + updatedOnClick() + } + .padding(8.dp), + ) { + val localEmphasis = DefaultEmphasisAlpha + val localTitleTextStyle = TextStyle( + color = LocalContentColor.current + .combineAlpha(localEmphasis) + .combineAlpha(MediumEmphasisAlpha), + ) + val localPriceTextStyle = TextStyle( + color = LocalContentColor.current + .combineAlpha(localEmphasis), + ) + CompositionLocalProvider( + LocalTextStyle provides MaterialTheme.typography.titleSmall + .merge(localTitleTextStyle), + ) { + title() + } + Spacer(modifier = Modifier.height(8.dp)) + CompositionLocalProvider( + LocalTextStyle provides MaterialTheme.typography.titleMedium + .merge(localPriceTextStyle), + ) { + price() + } + if (content != null) { + Spacer(modifier = Modifier.height(8.dp)) + content() + } + } } } - -@Composable -private fun SettingProductItem( - product: Product, -) { - val context by rememberUpdatedState(LocalLeContext) - FlatItemLayout( - leading = { - val backgroundColor = MaterialTheme.colorScheme.secondaryContainer - Box( - modifier = Modifier - .size(24.dp) - .background(backgroundColor, CircleShape) - .padding(4.dp), - ) { - val targetContentColor = kotlin.run { - val active = product.status is Product.Status.Active - if (active) { - MaterialTheme.colorScheme.primary - } else { - contentColorFor(backgroundColor) - } - } - val contentColor by animateColorAsState(targetValue = targetContentColor) - Icon( - Icons.Outlined.Star, - contentDescription = null, - tint = contentColor, - ) - } - }, - elevation = 2.dp, - trailing = { - ChevronIcon() - }, - content = { - FlatItemTextContent( - title = { - Text(product.price) - }, - text = { - Text(product.title) - }, - ) - - val statusOrNull = product.status as? Product.Status.Active - ExpandedIfNotEmpty(statusOrNull) { - Row( - modifier = Modifier - .padding(top = 4.dp), - ) { - Ah( - score = 1f, - text = stringResource(Res.string.pref_item_premium_status_active), - ) - } - } - }, - onClick = { - product.purchase(context) - }, - ) -}