Updated on 2026-08-14
This commit is contained in:
commit
5ca489c08b
6 changed files with 105 additions and 78 deletions
|
|
@ -146,38 +146,14 @@ internal class DefaultSingleAccountStatusListProducer @AssistedInject constructo
|
|||
flattenCurrency = flattenCurrency,
|
||||
)
|
||||
|
||||
val accounts = accountListFlow.value.accounts
|
||||
val hasPaymentAccount = accounts.any { it is Account.Payment }
|
||||
val hasVirtualAccount = accounts.any { it is Account.Virtual }
|
||||
logger.i("flattenFlow[$walletId]: payment=$hasPaymentAccount, virtual=$hasVirtualAccount")
|
||||
val specialStatusFlows = buildList<Flow<AccountStatus>> {
|
||||
if (hasPaymentAccount) {
|
||||
add(
|
||||
paymentAccountStatusSupplier.invoke(userWalletId = walletId)
|
||||
.onEach { status ->
|
||||
logger.i(
|
||||
"flattenFlow[$walletId]: paymentAccountStatus emitted " +
|
||||
"valueType=${status.value::class.simpleName}",
|
||||
val specialStatusesFlow: Flow<Map<AccountId, AccountStatus>> = combine(
|
||||
paymentAccountStatusSupplier.invoke(userWalletId = walletId),
|
||||
virtualAccountStatusSupplier.invoke(userWalletId = walletId),
|
||||
) { paymentStatus, virtualStatus ->
|
||||
mapOf(
|
||||
paymentStatus.accountId to paymentStatus,
|
||||
virtualStatus.accountId to virtualStatus,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
if (hasVirtualAccount) {
|
||||
add(
|
||||
virtualAccountStatusSupplier.invoke(userWalletId = walletId)
|
||||
.onEach { status ->
|
||||
logger.i(
|
||||
"flattenFlow[$walletId]: virtualAccountStatus emitted " +
|
||||
"valueType=${status.value::class.simpleName}",
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
val specialStatusesFlow: Flow<Map<AccountId, AccountStatus>> = if (specialStatusFlows.isEmpty()) {
|
||||
flowOf(emptyMap())
|
||||
} else {
|
||||
combine(specialStatusFlows) { statuses -> statuses.associateBy(AccountStatus::accountId) }
|
||||
}
|
||||
|
||||
combine(
|
||||
|
|
|
|||
|
|
@ -4,11 +4,7 @@ import androidx.compose.ui.text.SpanStyle
|
|||
import com.tangem.common.getRewardStakingBalance
|
||||
import com.tangem.common.getTotalStakingBalance
|
||||
import com.tangem.common.ui.earn.EarnBlockUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
|
@ -20,10 +16,12 @@ import com.tangem.domain.staking.model.StakingEntryInfo
|
|||
import com.tangem.domain.staking.model.StakingOption
|
||||
import com.tangem.domain.staking.model.common.RewardInfo
|
||||
import com.tangem.domain.staking.model.common.RewardType
|
||||
import com.tangem.domain.staking.model.optionOrNull
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isStakingRewardUnavailable
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -217,6 +215,7 @@ internal class UpdateStakingNotificationTransformer(
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun getRewardSubtitle(
|
||||
status: CryptoCurrencyStatus,
|
||||
stakingRewardAmount: BigDecimal?,
|
||||
|
|
@ -267,13 +266,23 @@ internal class UpdateStakingNotificationTransformer(
|
|||
}
|
||||
RewardBlockType.RewardsRequirementsError,
|
||||
RewardBlockType.Rewards,
|
||||
-> resourceReference(
|
||||
R.string.staking_details_rewards_to_claim,
|
||||
wrappedList(
|
||||
stakingRewardAmount.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
.orMaskWithStars(isBalanceHidden),
|
||||
),
|
||||
-> {
|
||||
val rewardAmount = stakingRewardAmount.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
.orMaskWithStars(isBalanceHidden)
|
||||
val rateReference = rewardRateReference()
|
||||
if (rateReference != null) {
|
||||
combinedReference(
|
||||
rateReference,
|
||||
stringReference(" ${StringsSigns.DOT} "),
|
||||
stringReference(rewardAmount),
|
||||
)
|
||||
} else {
|
||||
resourceReference(
|
||||
R.string.staking_details_rewards_to_claim,
|
||||
wrappedList(rewardAmount),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val isAccent = rewardBlockType == RewardBlockType.Rewards ||
|
||||
|
|
@ -285,6 +294,21 @@ internal class UpdateStakingNotificationTransformer(
|
|||
tone = if (isAccent) EarnBlockUM.SubtitleUM.Tone.Accent else EarnBlockUM.SubtitleUM.Tone.Disabled,
|
||||
)
|
||||
}
|
||||
|
||||
private fun rewardRateReference(): TextReference? {
|
||||
val rewardInfo = stakingAvailability.optionOrNull?.displayRewardInfo ?: return null
|
||||
val rateTypeResId = when (rewardInfo.type) {
|
||||
RewardType.APR -> CoreResR.string.staking_details_apr
|
||||
RewardType.APY,
|
||||
RewardType.UNKNOWN,
|
||||
-> CoreResR.string.staking_details_apy
|
||||
}
|
||||
return combinedReference(
|
||||
stringReference(rewardInfo.rate.format { percent() }),
|
||||
stringReference(" "),
|
||||
resourceReference(rateTypeResId),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun StakingBalance.Data?.hasPendingBalances(): Boolean = when (this) {
|
||||
|
|
|
|||
|
|
@ -11,29 +11,21 @@ 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.network.Network
|
||||
import com.tangem.domain.models.staking.BalanceItem
|
||||
import com.tangem.domain.models.staking.*
|
||||
import com.tangem.domain.models.staking.BalanceType
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.models.staking.StakingID
|
||||
import com.tangem.domain.models.staking.YieldBalanceItem
|
||||
import com.tangem.domain.models.staking.YieldToken
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.StakingOption
|
||||
import com.tangem.utils.StringsSigns.THREE_STARS
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.AddFundsUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM.TitleState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TransferUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.ZeroBalanceActionsUM
|
||||
import com.tangem.utils.StringsSigns.THREE_STARS
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.math.BigDecimal
|
||||
import com.tangem.core.res.R as CoreResR
|
||||
|
||||
class UpdateStakingNotificationTransformerTest {
|
||||
|
||||
|
|
@ -194,8 +186,40 @@ class UpdateStakingNotificationTransformerTest {
|
|||
assertThat(rewardFormatArg(subtitle.text)).isNotEqualTo(THREE_STARS)
|
||||
}
|
||||
|
||||
private fun rewardFormatArg(text: TextReference): Any? =
|
||||
(text as TextReference.Res).formatArgs.data.firstOrNull()
|
||||
@Test
|
||||
fun `GIVEN rewards to claim AND rate known WHEN transform THEN subtitle shows rate and reward amount`() {
|
||||
// Arrange
|
||||
val status = buildStatus(
|
||||
networkRawId = "ethereum",
|
||||
symbol = "ETH",
|
||||
isCoin = false,
|
||||
stakingBalance = stakeKitBalance(staked = BigDecimal("100"), rewards = BigDecimal("5")),
|
||||
)
|
||||
val transformer = createTransformer(
|
||||
availability = availableOption(BigDecimal("4.2")),
|
||||
entryInfo = StakingEntryInfo(tokenSymbol = "ETH"),
|
||||
status = status,
|
||||
isBalanceHidden = false,
|
||||
)
|
||||
|
||||
// Act
|
||||
val result = transformer.transform(initialState())
|
||||
|
||||
// Assert
|
||||
val content = result.earnBlockState as EarnBlockUM.Content
|
||||
val subtitle = content.subtitleUM as EarnBlockUM.SubtitleUM.Text
|
||||
val combined = subtitle.text as TextReference.Combined
|
||||
val rateLabel = combined.refs.data.first() as TextReference.Combined
|
||||
val apyLabel = rateLabel.refs.data.filterIsInstance<TextReference.Res>().first()
|
||||
assertThat(apyLabel.id).isEqualTo(CoreResR.string.staking_details_apy)
|
||||
assertThat((combined.refs.data.last() as TextReference.Str).value).isNotEqualTo(THREE_STARS)
|
||||
}
|
||||
|
||||
private fun rewardFormatArg(text: TextReference): Any? = when (text) {
|
||||
is TextReference.Res -> text.formatArgs.data.firstOrNull()
|
||||
is TextReference.Combined -> (text.refs.data.last() as? TextReference.Str)?.value
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun createTransformer(
|
||||
availability: StakingAvailability,
|
||||
|
|
@ -265,17 +289,16 @@ class UpdateStakingNotificationTransformerTest {
|
|||
)
|
||||
|
||||
private fun availableOption(apy: BigDecimal): StakingAvailability.Available {
|
||||
val option = mockk<StakingOption>(relaxed = true) {
|
||||
every { this@mockk.apy } returns apy
|
||||
}
|
||||
return StakingAvailability.Available(option = option)
|
||||
return StakingAvailability.Available(option = stakingOption(apy))
|
||||
}
|
||||
|
||||
private fun fullOption(apy: BigDecimal): StakingAvailability.Full {
|
||||
val option = mockk<StakingOption>(relaxed = true) {
|
||||
every { this@mockk.apy } returns apy
|
||||
return StakingAvailability.Full(option = stakingOption(apy))
|
||||
}
|
||||
return StakingAvailability.Full(option = option)
|
||||
|
||||
/** Deterministic concrete [StakingOption] whose [displayRewardInfo] resolves to [apy] with an APY rate. */
|
||||
private fun stakingOption(apy: BigDecimal): StakingOption.P2PEthPool = mockk(relaxed = true) {
|
||||
every { this@mockk.apy } returns apy
|
||||
}
|
||||
|
||||
private fun buildStatusWithStake(stakedAmount: BigDecimal): CryptoCurrencyStatus {
|
||||
|
|
|
|||
|
|
@ -7,12 +7,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED_WALLET_INDEX
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletType
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -271,7 +266,7 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
private fun WalletScreenState.incompleteActivationWalletIds(): List<UserWalletId> {
|
||||
return wallets.mapNotNull { wallet ->
|
||||
if (wallet.warnings.any { it is WalletNotification.FinishWalletActivation } ||
|
||||
wallet.walletCardState is WalletState.MultiCurrency &&
|
||||
wallet is WalletState.MultiCurrency &&
|
||||
wallet.walletCardState.additionalInfo?.isHotBackedUp == false
|
||||
) {
|
||||
wallet.walletCardState.id
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ import com.tangem.core.ui.components.marketprice.PriceChangeType
|
|||
import com.tangem.core.ui.ds.badge.*
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.styledResourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
|
@ -133,7 +136,11 @@ internal class WalletTokenCurrencyItemConverter(
|
|||
is CryptoCurrencyStatus.NoQuote,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
-> TangemTokenRowUM.SubtitleUM.Content(
|
||||
text = TextReference.EMPTY,
|
||||
text = stringReference(
|
||||
currencyStatus.value.fiatRate.format {
|
||||
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
|
||||
},
|
||||
),
|
||||
priceChangeUM = PriceChangeState.Content(
|
||||
type = PriceChangeType.fromBigDecimal(currencyStatus.value.priceChange.orZero()),
|
||||
valueInPercent = currencyStatus.value.priceChange.format { percent() },
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ 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.StringsSigns
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.core.res.R as CoreResR
|
||||
import com.tangem.core.ui.R as CoreUiR
|
||||
|
|
@ -80,9 +81,10 @@ internal class YieldSupplyToEarnBlockConverter : Converter<YieldSupplyUM, EarnBl
|
|||
iconUM = buildTitleIcon(value),
|
||||
),
|
||||
subtitleUM = EarnBlockUM.SubtitleUM.Text(
|
||||
text = resourceReference(
|
||||
id = CoreResR.string.yield_module_average_apy,
|
||||
formatArgs = wrappedList(value.apy),
|
||||
text = combinedReference(
|
||||
resourceReference(CoreResR.string.yield_module_earn_sheet_current_apy_title),
|
||||
stringReference(StringsSigns.WHITE_SPACE),
|
||||
stringReference(value.apy + StringsSigns.PERCENT),
|
||||
),
|
||||
style = EarnBlockUM.SubtitleUM.Style.Small,
|
||||
tone = EarnBlockUM.SubtitleUM.Tone.Accent,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue