Updated on 2026-08-14
This commit is contained in:
parent
29cbad3bb5
commit
12d4917dfc
5 changed files with 153 additions and 4 deletions
|
|
@ -21,7 +21,11 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.shadow.Shadow
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
|
|
@ -57,6 +61,7 @@ fun EarnBlock(state: EarnBlockUM, modifier: Modifier = Modifier) {
|
|||
when (state) {
|
||||
is EarnBlockUM.Loading -> EarnBlockLoading(modifier)
|
||||
is EarnBlockUM.Content -> EarnBlockContent(state, modifier)
|
||||
is EarnBlockUM.Promo -> EarnBlockPromo(state, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,6 +141,74 @@ private fun EarnBlockContent(state: EarnBlockUM.Content, modifier: Modifier = Mo
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EarnBlockPromo(state: EarnBlockUM.Promo, modifier: Modifier = Modifier) {
|
||||
val shape = RoundedCornerShape(TangemTheme.dimens2.x5)
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clip(shape)
|
||||
.backgroundModifier(state.type, state.backgroundUM, shape)
|
||||
.padding(all = TangemTheme.dimens2.x4),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x3),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
EarnBlockIcon(
|
||||
type = state.type,
|
||||
iconUM = state.iconUM,
|
||||
modifier = Modifier.padding(end = TangemTheme.dimens2.x3),
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Text(
|
||||
text = state.title.resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography2.bodyMedium16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
Text(
|
||||
text = state.subtitle.resolveReference(),
|
||||
style = TangemTheme.typography2.captionMedium12,
|
||||
color = state.type.accentText(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2)) {
|
||||
EarnBlockPromoButton(
|
||||
text = resourceReference(CoreResR.string.common_learn_more),
|
||||
type = TangemButtonType.Secondary,
|
||||
onClick = state.onSecondaryClick,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
EarnBlockPromoButton(
|
||||
text = resourceReference(CoreResR.string.common_activate),
|
||||
type = EarnBlockUM.TrailingUM.Button.Style.Default.buttonType(state.type),
|
||||
onClick = state.onPrimaryClick,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EarnBlockPromoButton(
|
||||
text: TextReference,
|
||||
type: TangemButtonType,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TangemButton(
|
||||
buttonUM = TangemButtonUM(
|
||||
text = text,
|
||||
type = type,
|
||||
size = TangemButtonSize.X9,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
onClick = onClick,
|
||||
),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Modifier.backgroundModifier(
|
||||
type: Type,
|
||||
|
|
@ -459,6 +532,22 @@ private class EarnBlockStakingPreviewProvider : CollectionPreviewParameterProvid
|
|||
|
||||
private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterProvider<EarnBlockUM>(
|
||||
collection = listOf(
|
||||
// Promo — boosted APY offer: AccentSoft background, two buttons below
|
||||
EarnBlockUM.Promo(
|
||||
type = Type.YieldSupply,
|
||||
backgroundUM = EarnBlockUM.BackgroundUM.AccentSoft,
|
||||
iconUM = EarnBlockUM.IconUM.Glowing(iconRes = R.drawable.ic_yield_40),
|
||||
title = annotatedReference(
|
||||
buildAnnotatedString {
|
||||
append("Special offer for Yield mode\nAPY ")
|
||||
withStyle(SpanStyle(textDecoration = TextDecoration.LineThrough)) { append("5.1%") }
|
||||
append(" x3 → 15.3%")
|
||||
},
|
||||
),
|
||||
subtitle = stringReference("First time activation bonus!"),
|
||||
onPrimaryClick = {},
|
||||
onSecondaryClick = {},
|
||||
),
|
||||
// Available — promo entry: AccentSoft background, "More" button
|
||||
EarnBlockUM.Content(
|
||||
type = Type.YieldSupply,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,21 @@ sealed interface EarnBlockUM {
|
|||
val onClick: (() -> Unit)? = null,
|
||||
) : EarnBlockUM
|
||||
|
||||
/**
|
||||
* Promo variant — a column with the [iconUM] + annotated multiline [title] + [subtitle] row on top
|
||||
* and a pair of full-width buttons below. Button labels are fixed and hardcoded in the composable,
|
||||
* so only their click handlers ([onSecondaryClick], [onPrimaryClick]) are exposed here.
|
||||
*/
|
||||
data class Promo(
|
||||
val type: Type,
|
||||
val backgroundUM: BackgroundUM,
|
||||
val iconUM: IconUM,
|
||||
val title: TextReference,
|
||||
val subtitle: TextReference,
|
||||
val onPrimaryClick: () -> Unit,
|
||||
val onSecondaryClick: () -> Unit,
|
||||
) : EarnBlockUM
|
||||
|
||||
enum class Type { Staking, YieldSupply }
|
||||
|
||||
@Immutable
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -70,7 +69,6 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
private val isYieldBoostPromoEnabledForTokenUseCase: IsYieldBoostPromoEnabledForTokenUseCase,
|
||||
private val getBoostedApyUseCase: GetBoostedApyUseCase,
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val boostStoryPreloader: YieldBoostStoryPreloader,
|
||||
) : Model(), YieldSupplyClickIntents {
|
||||
|
||||
|
|
@ -163,7 +161,6 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
yieldSupplyGetTokenStatusUseCase(cryptoCurrencyToken)
|
||||
.onRight { tokenStatus ->
|
||||
val isPromoEnabled = yieldSupplyFeatureToggles.isYieldPromoEnabled &&
|
||||
!designFeatureToggles.isRedesignEnabled &&
|
||||
isYieldBoostPromoEnabledForTokenUseCase(params.userWalletId, cryptoCurrencyToken)
|
||||
.getOrElse { false }
|
||||
val boostedApy = if (isPromoEnabled) getBoostedApyUseCase(tokenStatus.apy) else null
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.features.yield.supply.impl.main.model.converter
|
||||
|
||||
import com.tangem.common.ui.earn.EarnBlockUM
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
|
@ -21,7 +23,25 @@ internal class YieldSupplyToEarnBlockConverter : Converter<YieldSupplyUM, EarnBl
|
|||
is YieldSupplyUM.Loading -> EarnBlockUM.Loading
|
||||
}
|
||||
|
||||
private fun buildAvailable(value: YieldSupplyUM.Available): EarnBlockUM.Content {
|
||||
private fun buildAvailable(value: YieldSupplyUM.Available): EarnBlockUM = if (value.isBoostAvailable) {
|
||||
buildBoostedPromo(value)
|
||||
} else {
|
||||
buildAvailableContent(value)
|
||||
}
|
||||
|
||||
private fun buildBoostedPromo(value: YieldSupplyUM.Available): EarnBlockUM.Promo {
|
||||
return EarnBlockUM.Promo(
|
||||
type = EarnBlockUM.Type.YieldSupply,
|
||||
backgroundUM = EarnBlockUM.BackgroundUM.AccentSoft,
|
||||
iconUM = EarnBlockUM.IconUM.Glowing(iconRes = CoreUiR.drawable.ic_yield_40),
|
||||
title = combinedReference(value.title, stringReference("\n"), value.apyText),
|
||||
subtitle = resourceReference(CoreResR.string.yield_apy_boost_banner_subtitle),
|
||||
onPrimaryClick = value.onClick,
|
||||
onSecondaryClick = value.onLearnMoreClick,
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildAvailableContent(value: YieldSupplyUM.Available): EarnBlockUM.Content {
|
||||
return EarnBlockUM.Content(
|
||||
type = EarnBlockUM.Type.YieldSupply,
|
||||
backgroundUM = EarnBlockUM.BackgroundUM.AccentSoft,
|
||||
|
|
|
|||
|
|
@ -183,4 +183,32 @@ internal class YieldSupplyToEarnBlockConverterTest {
|
|||
content.onClick?.invoke()
|
||||
assertThat(clicked).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN Available isBoostAvailable WHEN convert THEN Promo with both button callbacks`() {
|
||||
var activateClicked = false
|
||||
var learnMoreClicked = false
|
||||
val available = YieldSupplyUM.Available(
|
||||
apy = "5.1",
|
||||
apyText = stringReference("APY 5.1% x3 → 15.3%"),
|
||||
title = stringReference("Special offer for Yield mode"),
|
||||
onClick = { activateClicked = true },
|
||||
onLearnMoreClick = { learnMoreClicked = true },
|
||||
isBoostAvailable = true,
|
||||
)
|
||||
|
||||
val result = converter.convert(available)
|
||||
|
||||
assertThat(result).isInstanceOf(EarnBlockUM.Promo::class.java)
|
||||
val promo = result as EarnBlockUM.Promo
|
||||
assertThat(promo.type).isEqualTo(EarnBlockUM.Type.YieldSupply)
|
||||
assertThat(promo.backgroundUM).isEqualTo(EarnBlockUM.BackgroundUM.AccentSoft)
|
||||
assertThat(promo.iconUM).isInstanceOf(EarnBlockUM.IconUM.Glowing::class.java)
|
||||
|
||||
promo.onSecondaryClick()
|
||||
assertThat(learnMoreClicked).isTrue()
|
||||
|
||||
promo.onPrimaryClick()
|
||||
assertThat(activateClicked).isTrue()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue