diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt index 75fcbfe749..20fb3ef396 100644 --- a/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt @@ -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> { - if (hasPaymentAccount) { - add( - paymentAccountStatusSupplier.invoke(userWalletId = walletId) - .onEach { status -> - logger.i( - "flattenFlow[$walletId]: paymentAccountStatus emitted " + - "valueType=${status.value::class.simpleName}", - ) - }, - ) - } - if (hasVirtualAccount) { - add( - virtualAccountStatusSupplier.invoke(userWalletId = walletId) - .onEach { status -> - logger.i( - "flattenFlow[$walletId]: virtualAccountStatus emitted " + - "valueType=${status.value::class.simpleName}", - ) - }, - ) - } - } - val specialStatusesFlow: Flow> = if (specialStatusFlows.isEmpty()) { - flowOf(emptyMap()) - } else { - combine(specialStatusFlows) { statuses -> statuses.associateBy(AccountStatus::accountId) } + val specialStatusesFlow: Flow> = combine( + paymentAccountStatusSupplier.invoke(userWalletId = walletId), + virtualAccountStatusSupplier.invoke(userWalletId = walletId), + ) { paymentStatus, virtualStatus -> + mapOf( + paymentStatus.accountId to paymentStatus, + virtualStatus.accountId to virtualStatus, + ) } combine( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformer.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformer.kt index 12dc90644f..21a31432a6 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformer.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformer.kt @@ -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) { diff --git a/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformerTest.kt b/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformerTest.kt index 09d0c4833b..7e312e66ee 100644 --- a/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformerTest.kt +++ b/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformerTest.kt @@ -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().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(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(relaxed = true) { - every { this@mockk.apy } returns apy - } - return StakingAvailability.Full(option = option) + return StakingAvailability.Full(option = stakingOption(apy)) + } + + /** 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 { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletsUpdateActionResolver.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletsUpdateActionResolver.kt index c632281590..e9c6b660ff 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletsUpdateActionResolver.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletsUpdateActionResolver.kt @@ -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 { 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 diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/WalletTokenCurrencyItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/WalletTokenCurrencyItemConverter.kt index 2e70a6332c..7f37f1b1e8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/WalletTokenCurrencyItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/WalletTokenCurrencyItemConverter.kt @@ -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() }, diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/converter/YieldSupplyToEarnBlockConverter.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/converter/YieldSupplyToEarnBlockConverter.kt index 17290cc94f..ed13583cad 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/converter/YieldSupplyToEarnBlockConverter.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/converter/YieldSupplyToEarnBlockConverter.kt @@ -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