Updated on 2026-08-14
This commit is contained in:
parent
e0fb0e3587
commit
4038e1b134
4 changed files with 118 additions and 65 deletions
|
|
@ -1,21 +1,33 @@
|
|||
package com.tangem.domain.models.currency
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
fun CryptoCurrency.Token.yieldSupplyKey(): String {
|
||||
return "${network.backendId}_$contractAddress"
|
||||
}
|
||||
|
||||
fun CryptoCurrencyStatus.yieldSupplyNotAllAmountSupplied(): Boolean {
|
||||
if (this.currency !is CryptoCurrency.Token) return false
|
||||
fun CryptoCurrencyStatus.hasNotSuppliedAmount(): Boolean {
|
||||
val notSupplied = notSuppliedAmountOrNull() ?: return false
|
||||
return notSupplied > BigDecimal.ZERO
|
||||
}
|
||||
|
||||
fun CryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(minAmount: BigDecimal): Boolean {
|
||||
val notSupplied = notSuppliedAmountOrNull() ?: return false
|
||||
return notSupplied >= minAmount
|
||||
}
|
||||
|
||||
fun CryptoCurrencyStatus.notSuppliedAmountOrNull(): BigDecimal? {
|
||||
if (this.currency !is CryptoCurrency.Token) return null
|
||||
|
||||
val supplyStatus = this.value.yieldSupplyStatus
|
||||
if (supplyStatus?.isActive != true) return false
|
||||
if (supplyStatus?.isActive != true) return null
|
||||
|
||||
val protocolBalance = supplyStatus.effectiveProtocolBalance
|
||||
val amount = this.value.amount
|
||||
|
||||
return if (protocolBalance != null && amount != null) {
|
||||
amount > protocolBalance
|
||||
amount.minus(protocolBalance)
|
||||
} else {
|
||||
false
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,8 @@ import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
|||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.yieldSupplyNotAllAmountSupplied
|
||||
import com.tangem.domain.models.currency.hasNotSuppliedAmount
|
||||
import com.tangem.domain.models.currency.shouldShowNotSuppliedInfoIcon
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||
|
|
@ -29,6 +30,7 @@ import com.tangem.domain.yield.supply.usecase.YieldSupplyActivateUseCase
|
|||
import com.tangem.domain.yield.supply.usecase.YieldSupplyDeactivateUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyIsAvailableUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyMinAmountUseCase
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyComponent
|
||||
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
|
|
@ -64,6 +66,7 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
private val yieldSupplyActivateUseCase: YieldSupplyActivateUseCase,
|
||||
private val yieldSupplyDeactivateUseCase: YieldSupplyDeactivateUseCase,
|
||||
private val yieldSupplyRepository: YieldSupplyRepository,
|
||||
private val yieldSupplyMinAmountUseCase: YieldSupplyMinAmountUseCase,
|
||||
) : Model(), YieldSupplyClickIntents {
|
||||
|
||||
private val params = paramsContainer.require<YieldSupplyComponent.Params>()
|
||||
|
|
@ -303,7 +306,10 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
private fun loadActiveState(cryptoCurrencyStatus: CryptoCurrencyStatus, yieldSupplyStatus: YieldSupplyStatus) {
|
||||
val cryptoCurrencyToken = cryptoCurrency as? CryptoCurrency.Token ?: return
|
||||
val showWarningIcon = !yieldSupplyStatus.isAllowedToSpend
|
||||
val showInfoIcon = cryptoCurrencyStatus.yieldSupplyNotAllAmountSupplied()
|
||||
val showInfoIconPrevState = when (uiState) {
|
||||
is YieldSupplyUM.Content -> uiState.showInfoIcon
|
||||
else -> false
|
||||
}
|
||||
if (!yieldSupplyStatus.isAllowedToSpend) {
|
||||
analyticsEventsHandler.send(
|
||||
YieldSupplyAnalytics.NoticeApproveNeeded(
|
||||
|
|
@ -331,10 +337,11 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
),
|
||||
onClick = ::onActiveClick,
|
||||
showWarningIcon = showWarningIcon,
|
||||
showInfoIcon = showInfoIcon,
|
||||
showInfoIcon = showInfoIconPrevState,
|
||||
apy = tokenStatus.apy.toString(),
|
||||
)
|
||||
}
|
||||
computeAndApplyShowInfoIcon(cryptoCurrencyStatus)
|
||||
}.onLeft {
|
||||
Timber.e(it)
|
||||
uiState.update {
|
||||
|
|
@ -348,14 +355,36 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
rewardsApy = TextReference.EMPTY,
|
||||
onClick = ::onActiveClick,
|
||||
showWarningIcon = showWarningIcon,
|
||||
showInfoIcon = showInfoIcon,
|
||||
showInfoIcon = showInfoIconPrevState,
|
||||
apy = "",
|
||||
)
|
||||
}
|
||||
computeAndApplyShowInfoIcon(cryptoCurrencyStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeAndApplyShowInfoIcon(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
val showInfoIcon = if (cryptoCurrencyStatus.hasNotSuppliedAmount()) {
|
||||
val minAmount = yieldSupplyMinAmountUseCase(userWallet, cryptoCurrencyStatus).getOrNull()
|
||||
if (minAmount != null) {
|
||||
cryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(minAmount)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
uiState.update { state ->
|
||||
when (state) {
|
||||
is YieldSupplyUM.Content -> state.copy(showInfoIcon = showInfoIcon)
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendInfoAboutProtocolStatus(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
if (lastYieldSupplyStatus == cryptoCurrencyStatus.value.yieldSupplyStatus) return
|
||||
val token = cryptoCurrency as? CryptoCurrency.Token ?: return
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
package com.tangem.features.yield.supply.impl.subcomponents.active.model
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
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.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
|
|
@ -16,7 +14,6 @@ import com.tangem.core.ui.format.bigdecimal.format
|
|||
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.yield.supply.usecase.YieldSupplyGetProtocolBalanceUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyMinAmountUseCase
|
||||
|
|
@ -31,13 +28,10 @@ import com.tangem.features.yield.supply.impl.subcomponents.active.model.transfor
|
|||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.transformer.update
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -121,7 +115,6 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
|||
|
||||
uiState.update {
|
||||
it.copy(
|
||||
notifications = getNotifications(cryptoCurrencyStatus),
|
||||
availableBalance = stringReference(
|
||||
protocolBalance.format {
|
||||
crypto(
|
||||
|
|
@ -156,54 +149,6 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getNotifications(cryptoCurrencyStatus: CryptoCurrencyStatus): ImmutableList<NotificationUM> {
|
||||
val approvalNotification = if (cryptoCurrencyStatus.value.yieldSupplyStatus?.isAllowedToSpend != true) {
|
||||
NotificationUM.Error(
|
||||
title = resourceReference(R.string.yield_module_approve_needed_notification_title),
|
||||
subtitle = resourceReference(R.string.yield_module_approve_needed_notification_description),
|
||||
iconResId = R.drawable.ic_alert_triangle_20,
|
||||
buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(R.string.yield_module_approve_needed_notification_cta),
|
||||
onClick = params.callback::onApprove,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val notSuppliedNotification = getNotSuppliedNotification(cryptoCurrencyStatus)
|
||||
return listOfNotNull(
|
||||
approvalNotification,
|
||||
notSuppliedNotification,
|
||||
).toPersistentList()
|
||||
}
|
||||
|
||||
private fun getNotSuppliedNotification(cryptoCurrencyStatus: CryptoCurrencyStatus): NotificationUM? {
|
||||
val value = cryptoCurrencyStatus.value
|
||||
val isActive = value.yieldSupplyStatus?.isActive == true
|
||||
val effectiveProtocolBalance = value.yieldSupplyStatus?.effectiveProtocolBalance ?: null
|
||||
val amount = value.amount
|
||||
|
||||
if (!isActive || effectiveProtocolBalance == null || amount == null) return null
|
||||
|
||||
val notDepositedAmount = amount.minus(effectiveProtocolBalance)
|
||||
return if (notDepositedAmount > BigDecimal.ZERO) {
|
||||
val formattedAmount =
|
||||
notDepositedAmount.format { crypto(symbol = "", decimals = cryptoCurrencyStatus.currency.decimals) }
|
||||
analyticsHandler.send(
|
||||
YieldSupplyAnalytics.NoticeAmountNotDeposited(
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
),
|
||||
)
|
||||
NotificationUM.Info.YieldSupplyNotAllAmountSupplied(
|
||||
formattedAmount = formattedAmount,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadMinAmount() {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
yieldSupplyMinAmountUseCase(
|
||||
|
|
@ -215,6 +160,8 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
|||
cryptoCurrencyStatus = cryptoCurrencyStatusFlow.value,
|
||||
appCurrency = appCurrency,
|
||||
minAmount = minAmount,
|
||||
analyticsHandler = analyticsHandler,
|
||||
onApprove = params.callback::onApprove,
|
||||
),
|
||||
)
|
||||
}.onLeft {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package com.tangem.features.yield.supply.impl.subcomponents.active.model.transformers
|
||||
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
|
|
@ -8,18 +11,37 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
|||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.notSuppliedAmountOrNull
|
||||
import com.tangem.domain.models.currency.shouldShowNotSuppliedInfoIcon
|
||||
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Computes and sets minimum amount (fiat) and fee description note
|
||||
* Transformer that populates minimum supply amount and related hints for the active Yield Supply screen.
|
||||
*
|
||||
* - Sets the displayed minimum amount in fiat and crypto.
|
||||
* - Builds the fee policy note text using the minimum amount.
|
||||
* - Adds contextual notifications:
|
||||
* - Approval required notification when spending is not yet allowed (emits analytics on CTA).
|
||||
* - "Not all amount supplied" info when wallet balance exceeds the supplied balance by more than [minAmount].
|
||||
*
|
||||
* @property cryptoCurrencyStatus Current currency status used to calculate values and flags.
|
||||
* @property appCurrency Preferred fiat currency for formatting.
|
||||
* @property minAmount Protocol-required minimal amount to deposit/supply (in crypto units).
|
||||
* @property analyticsHandler Analytics reporter for user actions.
|
||||
* @property onApprove Action invoked when the "Approve" notification button is tapped.
|
||||
*/
|
||||
internal class YieldSupplyActiveMinAmountTransformer(
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val minAmount: BigDecimal,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
private val onApprove: () -> Unit,
|
||||
) : Transformer<YieldSupplyActiveContentUM> {
|
||||
|
||||
override fun transform(prevState: YieldSupplyActiveContentUM): YieldSupplyActiveContentUM {
|
||||
|
|
@ -41,6 +63,49 @@ internal class YieldSupplyActiveMinAmountTransformer(
|
|||
return prevState.copy(
|
||||
minAmount = stringReference(minAmountFiatText),
|
||||
minFeeDescription = minFeeNoteValue,
|
||||
notifications = getNotifications(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getNotifications(): ImmutableList<NotificationUM> {
|
||||
val approvalNotification = if (cryptoCurrencyStatus.value.yieldSupplyStatus?.isAllowedToSpend != true) {
|
||||
NotificationUM.Error(
|
||||
title = resourceReference(R.string.yield_module_approve_needed_notification_title),
|
||||
subtitle = resourceReference(R.string.yield_module_approve_needed_notification_description),
|
||||
iconResId = R.drawable.ic_alert_triangle_20,
|
||||
buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(R.string.yield_module_approve_needed_notification_cta),
|
||||
onClick = onApprove,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val notSuppliedNotification = getNotSuppliedNotification(cryptoCurrencyStatus)
|
||||
return listOfNotNull(
|
||||
approvalNotification,
|
||||
notSuppliedNotification,
|
||||
).toPersistentList()
|
||||
}
|
||||
|
||||
private fun getNotSuppliedNotification(cryptoCurrencyStatus: CryptoCurrencyStatus): NotificationUM? {
|
||||
return if (cryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(minAmount)) {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val notDepositedAmount = cryptoCurrencyStatus.notSuppliedAmountOrNull()
|
||||
val formattedAmount =
|
||||
notDepositedAmount.format { crypto(symbol = "", decimals = cryptoCurrencyStatus.currency.decimals) }
|
||||
analyticsHandler.send(
|
||||
YieldSupplyAnalytics.NoticeAmountNotDeposited(
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
),
|
||||
)
|
||||
NotificationUM.Info.YieldSupplyNotAllAmountSupplied(
|
||||
formattedAmount = formattedAmount,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue