Updated on 2026-08-14
This commit is contained in:
parent
80ca0095f1
commit
7edea4da00
16 changed files with 467 additions and 160 deletions
|
|
@ -5,7 +5,6 @@ import com.tangem.blockchain.common.TransactionData
|
|||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Immutable
|
||||
internal sealed class YieldSupplyFeeUM {
|
||||
|
|
@ -31,5 +30,4 @@ internal data class YieldSupplyActionUM(
|
|||
val yieldSupplyFeeUM: YieldSupplyFeeUM,
|
||||
val isPrimaryButtonEnabled: Boolean,
|
||||
val isTransactionSending: Boolean,
|
||||
val maxFee: BigDecimal = BigDecimal.ZERO,
|
||||
)
|
||||
|
|
@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
|
|
@ -91,6 +92,7 @@ private fun SupplyUnavailable(modifier: Modifier = Modifier) {
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun SupplyContent(supplyUM: YieldSupplyUM.Content, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
|
|
@ -115,8 +117,11 @@ private fun SupplyContent(supplyUM: YieldSupplyUM.Content, modifier: Modifier =
|
|||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1.0f, fill = false),
|
||||
text = supplyUM.title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
AnimatedVisibility(supplyUM.rewardsApy != TextReference.EMPTY) {
|
||||
|
|
@ -344,17 +349,17 @@ private class PreviewProvider : PreviewParameterProvider<YieldSupplyUM> {
|
|||
onClick = {},
|
||||
),
|
||||
YieldSupplyUM.Content(
|
||||
title = stringReference("Aave lending is active"),
|
||||
title = stringReference("Aave l"),
|
||||
subtitle = stringReference("Interest accrues automatically"),
|
||||
rewardsApy = stringReference("5.1 % APY"),
|
||||
rewardsApy = stringReference("APY 5.1%"),
|
||||
onClick = {},
|
||||
apy = "5.1",
|
||||
showWarningIcon = false,
|
||||
),
|
||||
YieldSupplyUM.Content(
|
||||
title = stringReference("Aave lending is active"),
|
||||
title = stringReference("Aave lending is active "),
|
||||
subtitle = stringReference("Interest accrues automatically"),
|
||||
rewardsApy = stringReference("5.1 % APY"),
|
||||
rewardsApy = stringReference("APY 5.1%"),
|
||||
onClick = {},
|
||||
apy = "5.1",
|
||||
showWarningIcon = true,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -30,7 +32,6 @@ import com.tangem.features.yield.supply.impl.R
|
|||
import com.tangem.features.yield.supply.impl.promo.entity.YieldSupplyPromoUM
|
||||
import com.tangem.features.yield.supply.impl.promo.model.YieldSupplyPromoClickIntents
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
internal fun YieldSupplyPromoContent(
|
||||
yieldSupplyPromoUM: YieldSupplyPromoUM,
|
||||
|
|
@ -38,50 +39,18 @@ internal fun YieldSupplyPromoContent(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier
|
||||
.background(color = TangemTheme.colors.background.tertiary)
|
||||
.fillMaxWidth()
|
||||
.fillMaxSize()
|
||||
.imePadding()
|
||||
.systemBarsPadding()
|
||||
.padding(horizontal = 16.dp),
|
||||
.systemBarsPadding(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
YieldStatusAppBar(
|
||||
onBackClick = clickIntents::onBackClick,
|
||||
onHowItWorksClick = clickIntents::onHowItWorksClick,
|
||||
)
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(horizontal = 20.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_analytics_up_24),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.icon.accent.copy(0.1f), CircleShape)
|
||||
.padding(12.dp)
|
||||
.size(32.dp),
|
||||
)
|
||||
SpacerH(20.dp)
|
||||
Text(
|
||||
text = yieldSupplyPromoUM.title.resolveReference(),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerH8()
|
||||
Label(
|
||||
state = LabelUM(
|
||||
text = yieldSupplyPromoUM.subtitle,
|
||||
style = LabelStyle.REGULAR,
|
||||
icon = R.drawable.ic_information_24,
|
||||
onIconClick = clickIntents::onApyInfoClick,
|
||||
),
|
||||
)
|
||||
SpacerH32()
|
||||
PromoItems()
|
||||
}
|
||||
SpacerHMax()
|
||||
Content(yieldSupplyPromoUM = yieldSupplyPromoUM, clickIntents = clickIntents)
|
||||
YieldSupplyTosText(
|
||||
tosLink = yieldSupplyPromoUM.tosLink,
|
||||
policyLink = yieldSupplyPromoUM.policyLink,
|
||||
|
|
@ -90,16 +59,72 @@ internal fun YieldSupplyPromoContent(
|
|||
PrimaryButton(
|
||||
text = stringResourceSafe(R.string.yield_module_start_earning),
|
||||
onClick = clickIntents::onStartEarningClick,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 8.dp,
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun ColumnScope.Content(yieldSupplyPromoUM: YieldSupplyPromoUM, clickIntents: YieldSupplyPromoClickIntents) {
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(horizontal = 20.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_analytics_up_24),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.icon.accent.copy(0.1f), CircleShape)
|
||||
.padding(12.dp)
|
||||
.size(32.dp),
|
||||
)
|
||||
SpacerH(20.dp)
|
||||
Text(
|
||||
text = yieldSupplyPromoUM.title.resolveReference(),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerH8()
|
||||
Label(
|
||||
state = LabelUM(
|
||||
text = yieldSupplyPromoUM.subtitle,
|
||||
style = LabelStyle.REGULAR,
|
||||
icon = R.drawable.ic_information_24,
|
||||
onIconClick = clickIntents::onApyInfoClick,
|
||||
),
|
||||
)
|
||||
SpacerH32()
|
||||
PromoItems()
|
||||
}
|
||||
SpacerH32()
|
||||
}
|
||||
Fade(
|
||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
)
|
||||
SpacerH8()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun YieldStatusAppBar(onBackClick: () -> Unit, onHowItWorksClick: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier.padding(vertical = 16.dp),
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
|
|
@ -165,7 +190,12 @@ private fun PromoItem(@DrawableRes icon: Int, title: TextReference, subtitle: Te
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun YieldSupplyTosText(tosLink: String, policyLink: String, onClick: (String) -> Unit) {
|
||||
private fun YieldSupplyTosText(
|
||||
tosLink: String,
|
||||
policyLink: String,
|
||||
onClick: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val tosTitle = stringResourceSafe(R.string.common_terms_of_use)
|
||||
val policyTitle = stringResourceSafe(R.string.common_privacy_policy)
|
||||
val fullString = stringResourceSafe(id = R.string.yield_module_promo_screen_terms_disclaimer, tosTitle, policyTitle)
|
||||
|
|
@ -204,7 +234,7 @@ private fun YieldSupplyTosText(tosLink: String, policyLink: String, onClick: (St
|
|||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
|||
cryptoCurrencyStatus = cryptoStatus,
|
||||
appCurrency = appCurrency,
|
||||
feeValue = currentFee,
|
||||
maxNetworkFee = maxFee,
|
||||
maxNetworkFee = maxFee.second,
|
||||
analyticsHandler = analyticsHandler,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
|
||||
|
|
@ -62,8 +61,8 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
|||
private val yieldSupplyNotificationsUpdateTrigger: YieldSupplyNotificationsUpdateTrigger,
|
||||
private val yieldSupplyAlertFactory: YieldSupplyAlertFactory,
|
||||
private val yieldSupplyActivateUseCase: YieldSupplyActivateUseCase,
|
||||
private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase,
|
||||
private val yieldSupplyMinAmountUseCase: YieldSupplyMinAmountUseCase,
|
||||
private val yieldSupplyGetMaxFeeUseCase: YieldSupplyGetMaxFeeUseCase,
|
||||
private val yieldSupplyRepository: YieldSupplyRepository,
|
||||
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
|
||||
|
||||
|
|
@ -131,11 +130,8 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getMaxFee(): BigDecimal? {
|
||||
if (uiState.value.maxFee != BigDecimal.ZERO) return uiState.value.maxFee
|
||||
val yieldTokenStatus = yieldSupplyGetTokenStatusUseCase(cryptoCurrency as CryptoCurrency.Token)
|
||||
.getOrNull()
|
||||
return yieldTokenStatus?.maxFeeNative
|
||||
private suspend fun getMaxFeePair(): Pair<BigDecimal, BigDecimal>? {
|
||||
return yieldSupplyGetMaxFeeUseCase(userWallet, cryptoCurrencyStatus).getOrNull()
|
||||
}
|
||||
|
||||
private suspend fun onLoadFee() {
|
||||
|
|
@ -145,11 +141,10 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
|||
it.copy(yieldSupplyFeeUM = YieldSupplyFeeUM.Loading)
|
||||
}
|
||||
|
||||
val maxFee = if (uiState.value.maxFee == BigDecimal.ZERO) {
|
||||
getMaxFee()
|
||||
} else {
|
||||
uiState.value.maxFee
|
||||
} ?: return
|
||||
val maxFeePair = getMaxFeePair() ?: return
|
||||
|
||||
val maxFee = maxFeePair.first
|
||||
val maxFeeFiat = maxFeePair.second
|
||||
|
||||
val transactionListData = yieldSupplyStartEarningUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
|
|
@ -190,6 +185,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
|||
val feeSum = updatedTransactionList.sumOf {
|
||||
it.fee?.amount?.value.orZero()
|
||||
}
|
||||
|
||||
uiState.update(
|
||||
YieldSupplyStartEarningFeeContentTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatusFlow.value,
|
||||
|
|
@ -198,6 +194,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
|
|||
updatedTransactionList = updatedTransactionList,
|
||||
feeValue = feeSum,
|
||||
maxNetworkFee = maxFee,
|
||||
maxNetworkFeeFiat = maxFeeFiat,
|
||||
minAmount = minAmount,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ internal class YieldSupplyStartEarningFeeContentTransformer(
|
|||
private val updatedTransactionList: List<TransactionData.Uncompiled>,
|
||||
private val feeValue: BigDecimal,
|
||||
private val maxNetworkFee: BigDecimal,
|
||||
private val maxNetworkFeeFiat: BigDecimal,
|
||||
private val minAmount: BigDecimal,
|
||||
) : Transformer<YieldSupplyActionUM> {
|
||||
override fun transform(prevState: YieldSupplyActionUM): YieldSupplyActionUM {
|
||||
|
|
@ -42,14 +43,8 @@ internal class YieldSupplyStartEarningFeeContentTransformer(
|
|||
val tokenFiatFee = feeFiat // same fiat amount
|
||||
val tokenFiatFeeValueText = tokenFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
|
||||
val maxFiatFee = feeFiatRate?.let { rate ->
|
||||
maxNetworkFee.multiply(rate)
|
||||
}
|
||||
val maxFeeCryptoValueText = tokenFiatRate?.let { rate ->
|
||||
maxFiatFee?.divide(rate, cryptoCurrency.decimals, RoundingMode.HALF_UP)
|
||||
}.format { crypto(cryptoCurrency) }
|
||||
|
||||
val maxFiatFeeValueText = maxFiatFee.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
val maxFeeCryptoValueText = maxNetworkFee.format { crypto(cryptoCurrency) }
|
||||
val maxFiatFeeValueText = maxNetworkFeeFiat.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
|
||||
val minAmountCryptoText = minAmount.format { crypto(cryptoCurrency) }
|
||||
val minAmountFiat = tokenFiatRate?.let(minAmount::multiply)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
|
|||
val uiState: StateFlow<YieldSupplyActionUM>
|
||||
field: MutableStateFlow<YieldSupplyActionUM> = MutableStateFlow(
|
||||
YieldSupplyActionUM(
|
||||
title = resourceReference(R.string.yield_module_stop_earning),
|
||||
title = resourceReference(R.string.yield_module_stop_earning_sheet_title),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.yield_module_stop_earning_sheet_description,
|
||||
formatArgs = wrappedList(cryptoCurrency.symbol),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue