Updated on 2026-08-14
This commit is contained in:
parent
d30713a914
commit
cfd8ae2957
13 changed files with 209 additions and 140 deletions
|
|
@ -38,9 +38,8 @@ internal class DefaultYieldSupplyComponent @AssistedInject constructor(
|
|||
override fun Content(modifier: Modifier) {
|
||||
val yieldSupplyUM by model.uiState.collectAsStateWithLifecycle()
|
||||
val bottomSheet by bottomSheetSlot.subscribeAsState()
|
||||
val isBalanceHidden by model.isBalanceHiddenFlow.collectAsStateWithLifecycle()
|
||||
|
||||
YieldSupplyBlockContent(yieldSupplyUM = yieldSupplyUM, isBalanceHidden = isBalanceHidden, modifier = modifier)
|
||||
YieldSupplyBlockContent(yieldSupplyUM = yieldSupplyUM, modifier = modifier)
|
||||
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ internal sealed class YieldSupplyUM {
|
|||
data object Unavailable : YieldSupplyUM()
|
||||
|
||||
data class Content(
|
||||
val rewardsBalance: TextReference,
|
||||
val title: TextReference,
|
||||
val subtitle: TextReference,
|
||||
val rewardsApy: TextReference,
|
||||
val onClick: () -> Unit,
|
||||
val isAllowedToSpend: Boolean,
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@ import com.tangem.common.routing.AppRouter
|
|||
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.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
|
||||
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
|
|
@ -20,6 +23,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.features.yield.supply.impl.R
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyComponent
|
||||
import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM
|
||||
import com.tangem.features.yield.supply.impl.main.model.transformers.YieldSupplyTokenStatusSuccessTransformer
|
||||
|
|
@ -72,6 +76,8 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
val isBalanceHiddenFlow: StateFlow<Boolean>
|
||||
field = MutableStateFlow(false)
|
||||
|
||||
private var lastYieldSupplyStatus: YieldSupplyStatus? = null
|
||||
|
||||
init {
|
||||
checkIfYieldSupplyIsAvailable()
|
||||
}
|
||||
|
|
@ -100,7 +106,7 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
maybeCryptoCurrency.fold(
|
||||
ifRight = { cryptoCurrencyStatus ->
|
||||
cryptoCurrencyStatusFlow.update { cryptoCurrencyStatus }
|
||||
onDataLoaded(cryptoCurrencyStatus)
|
||||
onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus)
|
||||
},
|
||||
ifLeft = {
|
||||
Timber.w(it.toString())
|
||||
|
|
@ -128,6 +134,7 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
}.onLeft {
|
||||
Timber.e(it)
|
||||
uiState.update { YieldSupplyUM.Unavailable }
|
||||
}
|
||||
}
|
||||
|
|
@ -157,50 +164,76 @@ internal class YieldSupplyModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun onDataLoaded(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
@Suppress("MaximumLineLength")
|
||||
private fun onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
val yieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus
|
||||
val hasActiveTransaction = cryptoCurrencyStatus.value.hasCurrentNetworkTransactions
|
||||
val yieldTransaction = cryptoCurrencyStatus.value.pendingTransactions.firstOrNull {
|
||||
it.type is TxInfo.TransactionType.YieldSupply
|
||||
}?.type as? TxInfo.TransactionType.YieldSupply
|
||||
sendInfoAboutProtocolStatus(yieldSupplyStatus?.isActive == true)
|
||||
sendInfoAboutProtocolStatus(cryptoCurrencyStatus)
|
||||
|
||||
val yieldSupplyUM = when {
|
||||
when {
|
||||
hasActiveTransaction && yieldTransaction != null -> {
|
||||
coroutineScope.launch(dispatchers.io) {
|
||||
delay(PROCESSING_UPDATE_DELAY)
|
||||
fetchCurrencyStatusUseCase(userWalletId = userWallet.walletId, cryptoCurrency.id)
|
||||
}
|
||||
when (yieldTransaction) {
|
||||
TxInfo.TransactionType.YieldSupply.Enter -> YieldSupplyUM.Processing.Enter
|
||||
TxInfo.TransactionType.YieldSupply.Exit -> YieldSupplyUM.Processing.Exit
|
||||
uiState.update {
|
||||
when (yieldTransaction) {
|
||||
TxInfo.TransactionType.YieldSupply.Enter -> YieldSupplyUM.Processing.Enter
|
||||
TxInfo.TransactionType.YieldSupply.Exit -> YieldSupplyUM.Processing.Exit
|
||||
}
|
||||
}
|
||||
}
|
||||
yieldSupplyStatus?.isActive == true -> {
|
||||
val cryptoCurrencyToken = cryptoCurrency as? CryptoCurrency.Token ?: return
|
||||
modelScope.launch(dispatchers.default) {
|
||||
yieldSupplyGetTokenStatusUseCase(cryptoCurrencyToken)
|
||||
.onRight { tokenStatus ->
|
||||
uiState.update {
|
||||
YieldSupplyUM.Content(
|
||||
title = resourceReference(
|
||||
R.string.yield_module_token_details_earn_notification_earning_on_your_balance_title,
|
||||
),
|
||||
subtitle = resourceReference(
|
||||
R.string.yield_module_token_details_earn_notification_earning_on_your_balance_subtitle,
|
||||
),
|
||||
rewardsApy = combinedReference(
|
||||
resourceReference(
|
||||
R.string.yield_module_token_details_earn_notification_apy,
|
||||
),
|
||||
stringReference(tokenStatus.apy.toString() + "%"),
|
||||
),
|
||||
onClick = ::onActiveClick,
|
||||
isAllowedToSpend = yieldSupplyStatus.isAllowedToSpend,
|
||||
)
|
||||
}
|
||||
}.onLeft {
|
||||
Timber.e(it)
|
||||
uiState.update { YieldSupplyUM.Loading }
|
||||
}
|
||||
}
|
||||
}
|
||||
yieldSupplyStatus?.isActive == true ->
|
||||
YieldSupplyUM.Content(
|
||||
rewardsBalance = TextReference.EMPTY,
|
||||
rewardsApy = TextReference.EMPTY,
|
||||
onClick = ::onActiveClick,
|
||||
isAllowedToSpend = yieldSupplyStatus.isAllowedToSpend,
|
||||
)
|
||||
else -> YieldSupplyUM.Initial
|
||||
}
|
||||
|
||||
uiState.update { yieldSupplyUM }
|
||||
|
||||
when (yieldSupplyUM) {
|
||||
is YieldSupplyUM.Initial -> loadTokenStatus()
|
||||
else -> Unit
|
||||
else -> {
|
||||
loadTokenStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendInfoAboutProtocolStatus(isActivated: Boolean) {
|
||||
private fun sendInfoAboutProtocolStatus(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
if (lastYieldSupplyStatus == cryptoCurrencyStatus.value.yieldSupplyStatus) return
|
||||
val token = cryptoCurrency as? CryptoCurrency.Token ?: return
|
||||
modelScope.launch(dispatchers.default) {
|
||||
if (isActivated) {
|
||||
yieldSupplyActivateUseCase(token)
|
||||
if (cryptoCurrencyStatus.value.yieldSupplyStatus?.isActive == true) {
|
||||
yieldSupplyActivateUseCase(token).onRight {
|
||||
lastYieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus
|
||||
}
|
||||
} else {
|
||||
yieldSupplyDeactivateUseCase(token)
|
||||
yieldSupplyDeactivateUseCase(token).onRight {
|
||||
lastYieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.yield.supply.impl.main.ui
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -17,12 +18,14 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.SpacerW12
|
||||
import com.tangem.core.ui.components.SpacerW8
|
||||
import com.tangem.core.ui.components.TextShimmer
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
|
||||
|
|
@ -34,11 +37,7 @@ import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM
|
|||
import com.tangem.utils.StringsSigns
|
||||
|
||||
@Composable
|
||||
internal fun YieldSupplyBlockContent(
|
||||
yieldSupplyUM: YieldSupplyUM,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
internal fun YieldSupplyBlockContent(yieldSupplyUM: YieldSupplyUM, modifier: Modifier = Modifier) {
|
||||
AnimatedContent(
|
||||
targetState = yieldSupplyUM,
|
||||
modifier = modifier,
|
||||
|
|
@ -46,7 +45,7 @@ internal fun YieldSupplyBlockContent(
|
|||
when (supplyUM) {
|
||||
is YieldSupplyUM.Available -> SupplyAvailable(supplyUM)
|
||||
YieldSupplyUM.Loading -> SupplyLoading()
|
||||
is YieldSupplyUM.Content -> SupplyContent(supplyUM, isBalanceHidden)
|
||||
is YieldSupplyUM.Content -> SupplyContent(supplyUM)
|
||||
YieldSupplyUM.Processing.Enter -> SupplyProcessing(
|
||||
resourceReference(R.string.yield_module_token_details_earn_notification_processing),
|
||||
)
|
||||
|
|
@ -87,7 +86,7 @@ private fun SupplyUnavailable() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun SupplyContent(supplyUM: YieldSupplyUM.Content, isBalanceHidden: Boolean) {
|
||||
private fun SupplyContent(supplyUM: YieldSupplyUM.Content) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
|
|
@ -96,22 +95,21 @@ private fun SupplyContent(supplyUM: YieldSupplyUM.Content, isBalanceHidden: Bool
|
|||
.clickable(onClick = supplyUM.onClick)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.img_aave_22),
|
||||
modifier = Modifier.size(36.dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
SpacerW12()
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(
|
||||
R.string.yield_module_token_details_earn_notification_earning_on_your_balance_title,
|
||||
),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = supplyUM.rewardsBalance.orMaskWithStars(isBalanceHidden).resolveReference(),
|
||||
text = supplyUM.title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
|
|
@ -123,9 +121,15 @@ private fun SupplyContent(supplyUM: YieldSupplyUM.Content, isBalanceHidden: Bool
|
|||
Text(
|
||||
text = supplyUM.rewardsApy.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = supplyUM.subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
SpacerW8()
|
||||
AnimatedVisibility(supplyUM.isAllowedToSpend.not()) {
|
||||
|
|
@ -146,36 +150,42 @@ private fun SupplyContent(supplyUM: YieldSupplyUM.Content, isBalanceHidden: Bool
|
|||
|
||||
@Composable
|
||||
private fun SupplyProcessing(text: TextReference) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(
|
||||
R.string.yield_module_token_details_earn_notification_earning_on_your_balance_title,
|
||||
),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
Image(
|
||||
painter = painterResource(R.drawable.img_aave_22),
|
||||
modifier = Modifier.size(36.dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
SpacerW12()
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography.body1,
|
||||
text = stringResourceSafe(
|
||||
R.string.yield_module_token_details_earn_notification_earning_on_your_balance_title,
|
||||
),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(16.dp),
|
||||
color = TangemTheme.colors.icon.accent,
|
||||
strokeWidth = 2.dp,
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
SpacerW8()
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = TangemTheme.colors.icon.accent,
|
||||
strokeWidth = 2.dp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +296,7 @@ private fun SupplyInfo(
|
|||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun YieldSupplyBlockContent_Preview(@PreviewParameter(PreviewProvider::class) params: YieldSupplyUM) {
|
||||
TangemThemePreview {
|
||||
YieldSupplyBlockContent(params, true)
|
||||
YieldSupplyBlockContent(yieldSupplyUM = params, modifier = Modifier)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -301,13 +311,15 @@ private class PreviewProvider : PreviewParameterProvider<YieldSupplyUM> {
|
|||
onClick = {},
|
||||
),
|
||||
YieldSupplyUM.Content(
|
||||
rewardsBalance = stringReference("1 USDT"),
|
||||
title = stringReference("Aave lending is active"),
|
||||
subtitle = stringReference("Interest accrues automatically"),
|
||||
rewardsApy = stringReference("5.1 % APY"),
|
||||
onClick = {},
|
||||
isAllowedToSpend = false,
|
||||
),
|
||||
YieldSupplyUM.Content(
|
||||
rewardsBalance = stringReference("1 USDT"),
|
||||
title = stringReference("Aave lending is active"),
|
||||
subtitle = stringReference("Interest accrues automatically"),
|
||||
rewardsApy = stringReference("5.1 % APY"),
|
||||
onClick = {},
|
||||
isAllowedToSpend = true,
|
||||
|
|
|
|||
|
|
@ -56,17 +56,6 @@ internal fun YieldSupplyActiveContent(
|
|||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.yield_module_earn_sheet_total_earnings_title),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
ResizableText(
|
||||
text = state.totalEarnings.orMaskWithStars(isBalanceHidden).resolveReference(),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
|
||||
CurrentApy(state.apy)
|
||||
chartComponent.Content(Modifier.padding(bottom = 12.dp))
|
||||
}
|
||||
|
|
@ -85,22 +74,23 @@ internal fun YieldSupplyActiveContent(
|
|||
|
||||
@Composable
|
||||
private fun CurrentApy(apy: TextReference?, modifier: Modifier = Modifier) {
|
||||
Row(modifier = modifier.padding(vertical = 12.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Column(modifier = modifier.padding(vertical = 12.dp)) {
|
||||
Text(
|
||||
modifier = modifier.weight(1.0f),
|
||||
modifier = Modifier,
|
||||
text = stringResourceSafe(R.string.yield_module_earn_sheet_current_apy_title),
|
||||
style = TangemTheme.typography.body1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
AnimatedContent(
|
||||
modifier = Modifier.height(32.dp),
|
||||
targetState = apy?.resolveReference(),
|
||||
label = "CurrentApy",
|
||||
) { apyText ->
|
||||
if (apyText == null) {
|
||||
TextShimmer(
|
||||
modifier = modifier.width(56.dp),
|
||||
modifier = modifier.width(94.dp),
|
||||
text = "",
|
||||
style = TangemTheme.typography.body1,
|
||||
style = TangemTheme.typography.head,
|
||||
)
|
||||
} else {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
|
|
@ -108,7 +98,7 @@ private fun CurrentApy(apy: TextReference?, modifier: Modifier = Modifier) {
|
|||
painterResource(R.drawable.ic_arrow_up_8),
|
||||
tint = TangemTheme.colors.text.accent,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
modifier = Modifier.padding(end = 6.dp).size(12.dp),
|
||||
)
|
||||
Text(
|
||||
modifier = modifier,
|
||||
|
|
@ -260,6 +250,7 @@ private class YieldSupplyActiveBottomSheetPreviewProvider : PreviewParameterProv
|
|||
),
|
||||
subtitleLink = resourceReference(R.string.common_read_more),
|
||||
notificationUM = NotificationUM.Error.InvalidAmount,
|
||||
apy = stringReference("5,14%"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue