From ff3f1a26a5179e8e2be04ef0e813ba2ec03411a1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 25 Jun 2024 18:48:17 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../data/staking/DefaultStakingRepository.kt | 47 +++---- .../converters/YieldBalanceConverter.kt | 34 ++++- .../converters/YieldBalanceListConverter.kt | 21 ++- .../domain/staking/model/YieldBalance.kt | 17 ++- .../domain/staking/model/YieldBalanceList.kt | 18 ++- .../FetchStakingYieldBalanceUseCase.kt | 5 +- .../staking/GetStakingYieldBalanceUseCase.kt | 9 +- .../staking/repositories/StakingRepository.kt | 14 +- .../tokendetails/TokenDetailsPreviewData.kt | 26 ++-- .../tokendetails/state/StakingBlockState.kt | 10 +- .../state/TokenDetailsBalanceBlockState.kt | 8 +- ...TokenDetailsBalanceSelectStateConverter.kt | 75 ++++++++++ .../TokenDetailsLoadedBalanceConverter.kt | 130 +++++++++++++++--- .../state/factory/TokenDetailsStateFactory.kt | 24 ++-- .../tokendetails/state/utils/BalanceUtils.kt | 12 ++ .../tokendetails/ui/TokenDetailsScreen.kt | 21 +-- .../ui/components/TokenDetailsBalanceBlock.kt | 9 +- .../components/staking/StakingBalanceBlock.kt | 15 +- .../viewmodels/TokenDetailsViewModel.kt | 46 +++++-- 19 files changed, 420 insertions(+), 121 deletions(-) create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/utils/BalanceUtils.kt diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index 379877723a..0acc8d6b43 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt @@ -14,7 +14,7 @@ import com.tangem.datasource.local.token.StakingYieldsStore import com.tangem.domain.staking.model.* import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.tokens.model.CryptoCurrencyAddress import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow @@ -110,7 +110,7 @@ internal class DefaultStakingRepository( override suspend fun fetchSingleYieldBalance( userWalletId: UserWalletId, - networkStatus: NetworkStatus, + address: String, integrationId: String, refresh: Boolean, ) = withContext(dispatchers.io) { @@ -120,7 +120,7 @@ internal class DefaultStakingRepository( block = { val result = stakeKitApi.getSingleYieldBalance( integrationId = integrationId, - body = getBalanceRequestData(networkStatus), + body = getBalanceRequestData(address, integrationId), ).getOrThrow() stakingBalanceStore.store( @@ -136,20 +136,27 @@ internal class DefaultStakingRepository( override fun getSingleYieldBalanceFlow( userWalletId: UserWalletId, - networkStatus: NetworkStatus, + address: String, integrationId: String, - ): Flow> = channelFlow { + ): Flow = channelFlow { launch(dispatchers.io) { stakingBalanceStore.get(integrationId) .collectLatest { - send(yieldBalanceConverter.convertList(it)) + send( + yieldBalanceConverter.convert( + YieldBalanceConverter.Data( + balance = it, + integrationId = integrationId, + ), + ), + ) } } withContext(dispatchers.io) { fetchSingleYieldBalance( userWalletId, - networkStatus, + address, integrationId, ) } @@ -157,7 +164,7 @@ internal class DefaultStakingRepository( override suspend fun fetchMultiYieldBalance( userWalletId: UserWalletId, - networks: Set, + addresses: List, integrationId: String, refresh: Boolean, ) = withContext(dispatchers.io) { @@ -166,7 +173,7 @@ internal class DefaultStakingRepository( skipCache = refresh, block = { val result = stakeKitApi.getMultipleYieldBalances( - networks.map(::getBalanceRequestData), + addresses.map { getBalanceRequestData(it.address, integrationId) }, ).getOrThrow() stakingBalanceStore.store(result) @@ -176,18 +183,18 @@ internal class DefaultStakingRepository( override fun getMultiYieldBalanceFlow( userWalletId: UserWalletId, - networks: Set, + addresses: List, integrationId: String, - ): Flow> = channelFlow { + ): Flow = channelFlow { launch(dispatchers.io) { stakingBalanceStore.get() - .collectLatest { send(yieldBalanceListConverter.convertList(it)) } + .collectLatest { send(yieldBalanceListConverter.convert(it)) } } withContext(dispatchers.io) { fetchMultiYieldBalance( userWalletId, - networks, + addresses, integrationId, ) } @@ -202,23 +209,17 @@ internal class DefaultStakingRepository( return yields.map { yieldConverter.convert(it) } } - private fun getBalanceRequestData(networkStatus: NetworkStatus): YieldBalanceRequestBody { - val networkAddress = when (val network = networkStatus.value) { - NetworkStatus.MissedDerivation -> null - is NetworkStatus.NoAccount -> network.address - is NetworkStatus.Unreachable -> network.address - is NetworkStatus.Verified -> network.address - } - val address = networkAddress?.defaultAddress?.value.orEmpty() + private fun getBalanceRequestData(address: String, integrationId: String): YieldBalanceRequestBody { return YieldBalanceRequestBody( addresses = Address( address = address, additionalAddresses = null, // todo fill additional addresses metadata if needed - explorerUrl = "", // todo fill exporer url + explorerUrl = "", // todo fill exporer url [REDACTED_JIRA] ), args = YieldBalanceRequestBody.YieldBalanceRequestArgs( - validatorAddresses = listOf(), // todo add validator addresses + validatorAddresses = listOf(), // todo add validators [REDACTED_JIRA] ), + integrationId = integrationId, ) } diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceConverter.kt index 7a3182cc87..0ab969880e 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceConverter.kt @@ -1,16 +1,36 @@ package com.tangem.data.staking.converters import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO +import com.tangem.domain.staking.model.BalanceItem import com.tangem.domain.staking.model.BalanceType import com.tangem.domain.staking.model.YieldBalance +import com.tangem.domain.staking.model.YieldBalanceItem import com.tangem.utils.converter.Converter -internal class YieldBalanceConverter : Converter { - override fun convert(value: BalanceDTO): YieldBalance { - return YieldBalance( - type = BalanceType.valueOf(value.type.name), - amount = value.amount, - pricePerShare = value.pricePerShare, - ) +internal class YieldBalanceConverter : Converter { + + override fun convert(value: Data): YieldBalance { + return if (value.balance.isEmpty()) { + YieldBalance.Empty + } else { + YieldBalance.Data( + balance = YieldBalanceItem( + items = value.balance.map { item -> + BalanceItem( + type = BalanceType.valueOf(item.type.name), + amount = item.amount, + pricePerShare = item.pricePerShare, + rawCurrencyId = item.tokenDTO.coinGeckoId, + ) + }, + integrationId = value.integrationId, + ), + ) + } } + + data class Data( + val balance: List, + val integrationId: String?, + ) } \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceListConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceListConverter.kt index d0c4eea878..883ba21acd 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceListConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceListConverter.kt @@ -4,15 +4,26 @@ import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrap import com.tangem.domain.staking.model.YieldBalanceList import com.tangem.utils.converter.Converter -internal class YieldBalanceListConverter : Converter { +internal class YieldBalanceListConverter : Converter, YieldBalanceList> { internal val converter by lazy(LazyThreadSafetyMode.NONE) { YieldBalanceConverter() } - override fun convert(value: YieldBalanceWrapperDTO): YieldBalanceList { - return YieldBalanceList( - balances = converter.convertList(value.balances), - ) + override fun convert(value: List): YieldBalanceList { + return if (value.isEmpty()) { + YieldBalanceList.Empty + } else { + YieldBalanceList.Data( + balances = value.map { + converter.convert( + YieldBalanceConverter.Data( + balance = it.balances, + integrationId = it.integrationId, + ), + ) + }, + ) + } } } \ No newline at end of file diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/YieldBalance.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/YieldBalance.kt index 817743ac7c..1b22df4bed 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/YieldBalance.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/YieldBalance.kt @@ -2,10 +2,25 @@ package com.tangem.domain.staking.model import java.math.BigDecimal -data class YieldBalance( +sealed class YieldBalance { + + data class Data( + val balance: YieldBalanceItem, + ) : YieldBalance() + + data object Empty : YieldBalance() +} + +data class YieldBalanceItem( + val items: List, + val integrationId: String?, +) + +data class BalanceItem( val type: BalanceType, val amount: BigDecimal, val pricePerShare: BigDecimal, + val rawCurrencyId: String?, ) enum class BalanceType { diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/YieldBalanceList.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/YieldBalanceList.kt index 931e8fa879..22db0dfdf1 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/YieldBalanceList.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/YieldBalanceList.kt @@ -1,5 +1,17 @@ package com.tangem.domain.staking.model -data class YieldBalanceList( - val balances: List, -) \ No newline at end of file +sealed class YieldBalanceList { + + data class Data( + val balances: List, + ) : YieldBalanceList() { + fun getBalance(rawCurrencyId: String?): YieldBalance? { + return balances.firstOrNull { yield -> + (yield as? YieldBalance.Data)?.balance?.items + ?.any { it.rawCurrencyId == rawCurrencyId } == true + } + } + } + + data object Empty : YieldBalanceList() +} \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/FetchStakingYieldBalanceUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/FetchStakingYieldBalanceUseCase.kt index 46f0fdf101..bdbb1521d7 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/FetchStakingYieldBalanceUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/FetchStakingYieldBalanceUseCase.kt @@ -2,7 +2,6 @@ package com.tangem.domain.staking import arrow.core.Either import com.tangem.domain.staking.repositories.StakingRepository -import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.wallets.models.UserWalletId class FetchStakingYieldBalanceUseCase( @@ -11,13 +10,13 @@ class FetchStakingYieldBalanceUseCase( suspend operator fun invoke( userWalletId: UserWalletId, - networkStatus: NetworkStatus, + address: String, integrationId: String, refresh: Boolean = false, ): Either = Either.catch { stakingRepository.fetchSingleYieldBalance( userWalletId = userWalletId, - networkStatus = networkStatus, + address = address, integrationId = integrationId, refresh = refresh, ) diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingYieldBalanceUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingYieldBalanceUseCase.kt index 8821767a15..5e12367173 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingYieldBalanceUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingYieldBalanceUseCase.kt @@ -6,7 +6,6 @@ import arrow.core.right import com.tangem.domain.core.utils.EitherFlow import com.tangem.domain.staking.model.YieldBalance import com.tangem.domain.staking.repositories.StakingRepository -import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.map @@ -17,14 +16,14 @@ class GetStakingYieldBalanceUseCase( operator fun invoke( userWalletId: UserWalletId, - networkStatus: NetworkStatus, + address: String, integrationId: String, - ): EitherFlow> { + ): EitherFlow { return stakingRepository.getSingleYieldBalanceFlow( userWalletId = userWalletId, - networkStatus = networkStatus, + address = address, integrationId = integrationId, - ).map, Either>> { it.right() } + ).map> { it.right() } .catch { emit(it.left()) } } } \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt index 5eb34854c5..3a81eb1ae7 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt @@ -2,7 +2,7 @@ package com.tangem.domain.staking.repositories import com.tangem.domain.staking.model.* import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.tokens.model.CryptoCurrencyAddress import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow @@ -23,27 +23,27 @@ interface StakingRepository { suspend fun fetchSingleYieldBalance( userWalletId: UserWalletId, - networkStatus: NetworkStatus, + address: String, integrationId: String, refresh: Boolean = false, ) fun getSingleYieldBalanceFlow( userWalletId: UserWalletId, - networkStatus: NetworkStatus, + address: String, integrationId: String, - ): Flow> + ): Flow suspend fun fetchMultiYieldBalance( userWalletId: UserWalletId, - networks: Set, + addresses: List, integrationId: String, refresh: Boolean = false, ) fun getMultiYieldBalanceFlow( userWalletId: UserWalletId, - networks: Set, + addresses: List, integrationId: String, - ): Flow> + ): Flow } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt index 79b16936c9..132a42865d 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt @@ -19,6 +19,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.component import com.tangem.features.tokendetails.impl.R import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow +import java.math.BigDecimal @Suppress("LargeClass") internal object TokenDetailsPreviewData { @@ -121,12 +122,13 @@ internal object TokenDetailsPreviewData { ) val balanceContent = TokenDetailsBalanceBlockState.Content( actionButtons = actionButtons, - fiatBalance = "91,50$", - cryptoBalance = "966,96 XLM", - isStakingEnabled = true, + fiatBalance = BigDecimal.ZERO, + cryptoBalance = BigDecimal.ZERO, balanceSegmentedButtonConfig = balanceSegmentedButtonConfig, selectedBalanceType = BalanceType.ALL, onBalanceSelect = {}, + displayCryptoBalance = "966,96 XLM", + displayFiatBalance = "91,50$", ) val balanceError = TokenDetailsBalanceBlockState.Error( actionButtons = actionButtons, @@ -139,9 +141,12 @@ internal object TokenDetailsPreviewData { private val stakingLoading = StakingBlocksState( stakingAvailable = StakingAvailable.Loading(iconState), stakingBalance = StakingBalance.Content( - cryptoAmount = stringReference("5 SOL"), - fiatAmount = stringReference("456.34 $"), - rewardAmount = resourceReference(R.string.staking_details_no_rewards_to_claim, wrappedList("0.43 $")), + cryptoValue = stringReference("5 SOL"), + fiatValue = stringReference("456.34 $"), + rewardValue = resourceReference(R.string.staking_details_no_rewards_to_claim, wrappedList("0.43 $")), + cryptoAmount = BigDecimal.ZERO, + fiatAmount = BigDecimal.ZERO, + onStakeClicked = {}, ), ) @@ -320,9 +325,12 @@ internal object TokenDetailsPreviewData { onStakeClicked = {}, ), stakingBalance = StakingBalance.Content( - cryptoAmount = stringReference("5 SOL"), - fiatAmount = stringReference("456.34 $"), - rewardAmount = resourceReference(R.string.staking_details_rewards_to_claim, wrappedList("0.43 $")), + cryptoValue = stringReference("5 SOL"), + fiatValue = stringReference("456.34 $"), + rewardValue = resourceReference(R.string.staking_details_no_rewards_to_claim, wrappedList("0.43 $")), + cryptoAmount = BigDecimal.ZERO, + fiatAmount = BigDecimal.ZERO, + onStakeClicked = {}, ), ), notifications = persistentListOf(), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockState.kt index 6b5802a796..d1a635dd87 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockState.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockState.kt @@ -2,6 +2,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state import androidx.compose.runtime.Immutable import com.tangem.core.ui.extensions.TextReference +import java.math.BigDecimal internal data class StakingBlocksState( val stakingAvailable: StakingAvailable, @@ -30,8 +31,11 @@ sealed class StakingBalance { data object Empty : StakingBalance() data class Content( - val cryptoAmount: TextReference, - val fiatAmount: TextReference, - val rewardAmount: TextReference, + val cryptoValue: TextReference, + val fiatValue: TextReference, + val rewardValue: TextReference, + val cryptoAmount: BigDecimal?, + val fiatAmount: BigDecimal?, + val onStakeClicked: () -> Unit, ) : StakingBalance() } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsBalanceBlockState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsBalanceBlockState.kt index 21ee681241..825f45f6c5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsBalanceBlockState.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsBalanceBlockState.kt @@ -2,6 +2,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton import kotlinx.collections.immutable.ImmutableList +import java.math.BigDecimal internal sealed class TokenDetailsBalanceBlockState { @@ -19,10 +20,11 @@ internal sealed class TokenDetailsBalanceBlockState { override val actionButtons: ImmutableList, override val balanceSegmentedButtonConfig: ImmutableList, override val selectedBalanceType: BalanceType, - val fiatBalance: String, - val cryptoBalance: String, - val isStakingEnabled: Boolean, + val fiatBalance: BigDecimal?, + val cryptoBalance: BigDecimal?, val onBalanceSelect: (TokenBalanceSegmentedButtonConfig) -> Unit, + val displayCryptoBalance: String, + val displayFiatBalance: String, ) : TokenDetailsBalanceBlockState() data class Error( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt new file mode 100644 index 0000000000..a42e0f24d6 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt @@ -0,0 +1,75 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory + +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.feature.tokendetails.presentation.tokendetails.state.* +import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance +import com.tangem.utils.Provider +import com.tangem.utils.converter.Converter +import java.math.BigDecimal + +internal class TokenDetailsBalanceSelectStateConverter( + private val currentStateProvider: Provider, + private val appCurrencyProvider: Provider, + private val cryptoCurrencyStatusProvider: Provider, +) : Converter { + + override fun convert(value: TokenBalanceSegmentedButtonConfig): TokenDetailsState { + return with(currentStateProvider()) { + if (stakingBlocksState.stakingBalance !is StakingBalance.Content) return this + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() ?: return this + + val stakingCryptoAmount = stakingBlocksState.stakingBalance.cryptoAmount + val stakingFiatAmount = stakingBlocksState.stakingBalance.fiatAmount + + copy( + tokenBalanceBlockState = if (tokenBalanceBlockState is TokenDetailsBalanceBlockState.Content) { + tokenBalanceBlockState.copy( + selectedBalanceType = value.type, + displayFiatBalance = formatFiatAmount( + status = cryptoCurrencyStatus.value, + stakingFiatAmount = stakingFiatAmount, + selectedBalanceType = value.type, + appCurrency = appCurrencyProvider(), + ), + displayCryptoBalance = formatCryptoAmount( + status = cryptoCurrencyStatus, + stakingCryptoAmount = stakingCryptoAmount, + selectedBalanceType = value.type, + ), + ) + } else { + tokenBalanceBlockState + }, + ) + } + } + + private fun formatFiatAmount( + status: CryptoCurrencyStatus.Value, + stakingFiatAmount: BigDecimal?, + selectedBalanceType: BalanceType, + appCurrency: AppCurrency, + ): String { + val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN + val totalAmount = fiatAmount.getBalance(selectedBalanceType, stakingFiatAmount) + + return BigDecimalFormatter.formatFiatAmount( + fiatAmount = totalAmount, + fiatCurrencyCode = appCurrency.code, + fiatCurrencySymbol = appCurrency.symbol, + ) + } + + private fun formatCryptoAmount( + status: CryptoCurrencyStatus, + stakingCryptoAmount: BigDecimal?, + selectedBalanceType: BalanceType, + ): String { + val amount = status.value.amount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN + val totalAmount = amount.getBalance(selectedBalanceType, stakingCryptoAmount) + + return BigDecimalFormatter.formatCryptoAmount(totalAmount, status.currency.symbol, status.currency.decimals) + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index 87e2d83683..81e8ef5fc0 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -6,41 +6,54 @@ import com.tangem.core.ui.components.marketprice.PriceChangeState import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter import com.tangem.core.ui.components.transactions.state.TxHistoryState +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.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.staking.model.YieldBalance import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.feature.tokendetails.presentation.tokendetails.state.BalanceType -import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState -import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.* import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsTxHistoryTransactionStateConverter +import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents +import com.tangem.features.tokendetails.impl.R import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList +import java.math.BigDecimal internal class TokenDetailsLoadedBalanceConverter( private val currentStateProvider: Provider, private val appCurrencyProvider: Provider, - private val isStakingEnabled: Boolean, private val symbol: String, private val decimals: Int, private val clickIntents: TokenDetailsClickIntents, -) : Converter, TokenDetailsState> { +) : Converter { private val txHistoryItemConverter by lazy { TokenDetailsTxHistoryTransactionStateConverter(symbol, decimals, clickIntents) } - override fun convert(value: Either): TokenDetailsState { - return value.fold(ifLeft = { convertError() }, ifRight = ::convert) + data class Data( + val maybeCryptoCurrencyStatus: Either, + val maybeYieldBalance: Either?, + ) + + override fun convert(value: Data): TokenDetailsState { + return value.maybeCryptoCurrencyStatus.fold( + ifLeft = { convertError() }, + ifRight = { convert(it, value.maybeYieldBalance?.getOrNull() ?: YieldBalance.Empty) }, + ) } private fun convertError(): TokenDetailsState { val state = currentStateProvider() return state.copy( + isStakingBlockShown = false, tokenBalanceBlockState = TokenDetailsBalanceBlockState.Error( actionButtons = state.tokenBalanceBlockState.actionButtons, balanceSegmentedButtonConfig = state.tokenBalanceBlockState.balanceSegmentedButtonConfig, @@ -51,12 +64,23 @@ internal class TokenDetailsLoadedBalanceConverter( ) } - private fun convert(status: CryptoCurrencyStatus): TokenDetailsState { + private fun convert(status: CryptoCurrencyStatus, yieldBalance: YieldBalance): TokenDetailsState { val state = currentStateProvider() val currencyName = state.marketPriceBlockState.currencySymbol val pendingTxs = status.value.pendingTransactions.map(txHistoryItemConverter::convert).toPersistentList() + val stakingCryptoAmount = (yieldBalance as? YieldBalance.Data)?.let { + yieldBalance.balance.items.sumOf { it.amount } + } + val stakingFiatAmount = stakingCryptoAmount?.let { status.value.fiatRate?.multiply(it) } + return state.copy( - tokenBalanceBlockState = getBalanceState(state.tokenBalanceBlockState, status), + tokenBalanceBlockState = getBalanceState( + currentState = state.tokenBalanceBlockState, + status = status, + stakingCryptoAmount = stakingCryptoAmount, + stakingFiatAmount = stakingFiatAmount, + ), + stakingBlocksState = getYieldBalance(status, yieldBalance, state), marketPriceBlockState = getMarketPriceState(status = status.value, currencySymbol = currencyName), pendingTxs = pendingTxs, txHistoryState = if (state.txHistoryState is TxHistoryState.NotSupported) { @@ -70,6 +94,8 @@ internal class TokenDetailsLoadedBalanceConverter( private fun getBalanceState( currentState: TokenDetailsBalanceBlockState, status: CryptoCurrencyStatus, + stakingCryptoAmount: BigDecimal?, + stakingFiatAmount: BigDecimal?, ): TokenDetailsBalanceBlockState { return when (status.value) { is CryptoCurrencyStatus.NoQuote, @@ -78,9 +104,19 @@ internal class TokenDetailsLoadedBalanceConverter( is CryptoCurrencyStatus.Custom, -> TokenDetailsBalanceBlockState.Content( actionButtons = currentState.actionButtons, - fiatBalance = formatFiatAmount(status.value, appCurrencyProvider()), - cryptoBalance = formatCryptoAmount(status), - isStakingEnabled = isStakingEnabled, + cryptoBalance = status.value.amount, + fiatBalance = status.value.fiatAmount, + displayFiatBalance = formatFiatAmount( + status.value, + stakingFiatAmount, + currentState.selectedBalanceType, + appCurrencyProvider(), + ), + displayCryptoBalance = formatCryptoAmount( + status, + stakingCryptoAmount, + currentState.selectedBalanceType, + ), balanceSegmentedButtonConfig = currentState.balanceSegmentedButtonConfig, onBalanceSelect = clickIntents::onBalanceSelect, selectedBalanceType = currentState.selectedBalanceType, @@ -88,7 +124,7 @@ internal class TokenDetailsLoadedBalanceConverter( is CryptoCurrencyStatus.Loading -> TokenDetailsBalanceBlockState.Loading( actionButtons = currentState.actionButtons, balanceSegmentedButtonConfig = currentState.balanceSegmentedButtonConfig, - selectedBalanceType = BalanceType.ALL, + selectedBalanceType = currentState.selectedBalanceType, ) is CryptoCurrencyStatus.MissedDerivation, is CryptoCurrencyStatus.Unreachable, @@ -96,11 +132,58 @@ internal class TokenDetailsLoadedBalanceConverter( -> TokenDetailsBalanceBlockState.Error( actionButtons = currentState.actionButtons, balanceSegmentedButtonConfig = currentState.balanceSegmentedButtonConfig, - selectedBalanceType = BalanceType.ALL, + selectedBalanceType = currentState.selectedBalanceType, ) } } + private fun getYieldBalance( + status: CryptoCurrencyStatus, + yieldBalance: YieldBalance, + state: TokenDetailsState, + ): StakingBlocksState { + val stakingCryptoAmount = (yieldBalance as? YieldBalance.Data)?.let { + yieldBalance.balance.items.sumOf { it.amount } + } + val stakingFiatAmount = stakingCryptoAmount?.let { status.value.fiatRate?.multiply(it) } + val stakingRewardAmount = (yieldBalance as? YieldBalance.Data)?.let { + yieldBalance.balance.items.sumOf { it.amount.multiply(it.pricePerShare) } + } + val stakingBalance = if (stakingCryptoAmount == null) { + StakingBalance.Empty + } else { + StakingBalance.Content( + cryptoAmount = stakingCryptoAmount, + fiatAmount = stakingFiatAmount, + cryptoValue = stringReference( + BigDecimalFormatter.formatCryptoAmount(stakingCryptoAmount, symbol, decimals), + ), + fiatValue = stringReference( + BigDecimalFormatter.formatFiatAmount( + stakingFiatAmount, + appCurrencyProvider().code, + appCurrencyProvider().symbol, + ), + ), + rewardValue = resourceReference( + R.string.staking_details_rewards_to_claim, + wrappedList( + BigDecimalFormatter.formatFiatAmount( + stakingRewardAmount, + appCurrencyProvider().code, + appCurrencyProvider().symbol, + ), + ), + ), + onStakeClicked = clickIntents::onStakeBannerClick, + ) + } + return state.stakingBlocksState.copy( + stakingAvailable = state.stakingBlocksState.stakingAvailable, + stakingBalance = stakingBalance, + ) + } + private fun getMarketPriceState( status: CryptoCurrencyStatus.Value, currencySymbol: String, @@ -158,19 +241,30 @@ internal class TokenDetailsLoadedBalanceConverter( ) } - private fun formatFiatAmount(status: CryptoCurrencyStatus.Value, appCurrency: AppCurrency): String { + private fun formatFiatAmount( + status: CryptoCurrencyStatus.Value, + stakingFiatAmount: BigDecimal?, + selectedBalanceType: BalanceType, + appCurrency: AppCurrency, + ): String { val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN + val totalAmount = fiatAmount.getBalance(selectedBalanceType, stakingFiatAmount) return BigDecimalFormatter.formatFiatAmount( - fiatAmount = fiatAmount, + fiatAmount = totalAmount, fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol, ) } - private fun formatCryptoAmount(status: CryptoCurrencyStatus): String { + private fun formatCryptoAmount( + status: CryptoCurrencyStatus, + stakingCryptoAmount: BigDecimal?, + selectedBalanceType: BalanceType, + ): String { val amount = status.value.amount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN + val totalAmount = amount.getBalance(selectedBalanceType, stakingCryptoAmount) - return BigDecimalFormatter.formatCryptoAmount(amount, status.currency.symbol, status.currency.decimals) + return BigDecimalFormatter.formatCryptoAmount(totalAmount, status.currency.symbol, status.currency.decimals) } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index ecb6639506..7e860743ac 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -18,6 +18,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo +import com.tangem.domain.staking.model.YieldBalance import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.* import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning @@ -42,9 +43,9 @@ import kotlinx.coroutines.flow.MutableStateFlow internal class TokenDetailsStateFactory( private val currentStateProvider: Provider, private val appCurrencyProvider: Provider, + private val cryptoCurrencyStatusProvider: Provider, private val clickIntents: TokenDetailsClickIntents, private val featureToggles: TokenDetailsFeatureToggles, - private val isStakingEnabled: Boolean, symbol: String, decimals: Int, ) { @@ -64,7 +65,6 @@ internal class TokenDetailsStateFactory( TokenDetailsLoadedBalanceConverter( currentStateProvider = currentStateProvider, appCurrencyProvider = appCurrencyProvider, - isStakingEnabled = isStakingEnabled, symbol = symbol, decimals = decimals, clickIntents = clickIntents, @@ -107,14 +107,25 @@ internal class TokenDetailsStateFactory( ) } + private val balanceSelectStateConverter by lazy { + TokenDetailsBalanceSelectStateConverter( + currentStateProvider = currentStateProvider, + appCurrencyProvider = appCurrencyProvider, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + ) + } + fun getInitialState(screenArgument: CryptoCurrency): TokenDetailsState { return skeletonStateConverter.convert(value = screenArgument) } fun getCurrencyLoadedBalanceState( cryptoCurrencyEither: Either, + yieldBalanceEither: Either?, ): TokenDetailsState { - return tokenDetailsLoadedBalanceConverter.convert(cryptoCurrencyEither) + return tokenDetailsLoadedBalanceConverter.convert( + TokenDetailsLoadedBalanceConverter.Data(cryptoCurrencyEither, yieldBalanceEither), + ) } fun getManageButtonsState(actions: List): TokenDetailsState { @@ -352,12 +363,7 @@ internal class TokenDetailsStateFactory( fun getStateWithUpdatedBalanceSegmentedButtonConfig( buttonConfig: TokenBalanceSegmentedButtonConfig, ): TokenDetailsState { - return with(currentStateProvider()) { - val updatedState = (tokenBalanceBlockState as? TokenDetailsBalanceBlockState.Content) - ?.copy(selectedBalanceType = buttonConfig.type) - ?: tokenBalanceBlockState - copy(tokenBalanceBlockState = updatedState) - } + return balanceSelectStateConverter.convert(buttonConfig) } private fun TokenDetailsAppBarMenuConfig.updateMenu( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/utils/BalanceUtils.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/utils/BalanceUtils.kt new file mode 100644 index 0000000000..faa0144943 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/utils/BalanceUtils.kt @@ -0,0 +1,12 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.state.utils + +import com.tangem.feature.tokendetails.presentation.tokendetails.state.BalanceType +import java.math.BigDecimal + +fun BigDecimal.getBalance(selectedBalanceType: BalanceType, stakingAmount: BigDecimal?): BigDecimal { + return if (selectedBalanceType == BalanceType.ALL && stakingAmount != null) { + this.plus(stakingAmount) + } else { + this + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt index f076f05240..638e43d98d 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt @@ -108,6 +108,7 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) { modifier = itemModifier, isBalanceHidden = state.isBalanceHidden, state = state.tokenBalanceBlockState, + isStakingAvailable = state.isStakingBlockShown, ) } items( @@ -141,16 +142,6 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) { } if (state.isStakingBlockShown) { - item( - key = StakingAvailable::class.java, - contentType = StakingAvailable::class.java, - content = { - TokenStakingBlock( - modifier = itemModifier, - state = state.stakingBlocksState.stakingAvailable, - ) - }, - ) if (state.stakingBlocksState.stakingBalance is StakingBalance.Content) { item( key = StakingBalance::class.java, @@ -163,6 +154,16 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) { }, ) } + item( + key = StakingAvailable::class.java, + contentType = StakingAvailable::class.java, + content = { + TokenStakingBlock( + modifier = itemModifier, + state = state.stakingBlocksState.stakingAvailable, + ) + }, + ) } swapTransactionsItems( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt index 081fab085e..ac4738e499 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt @@ -29,6 +29,7 @@ import kotlinx.collections.immutable.toImmutableList internal fun TokenDetailsBalanceBlock( state: TokenDetailsBalanceBlockState, isBalanceHidden: Boolean, + isStakingAvailable: Boolean, modifier: Modifier = Modifier, ) { Surface( @@ -53,7 +54,7 @@ internal fun TokenDetailsBalanceBlock( .weight(1f) .padding(top = TangemTheme.dimens.spacing12), ) - BalanceButtons(state) + if (isStakingAvailable) BalanceButtons(state) } FiatBalance( state = state, @@ -94,7 +95,7 @@ private fun FiatBalance( ) is TokenDetailsBalanceBlockState.Content -> Text( modifier = modifier, - text = if (isBalanceHidden) STARS else state.fiatBalance, + text = if (isBalanceHidden) STARS else state.displayFiatBalance, style = TangemTheme.typography.h2, color = TangemTheme.colors.text.primary1, ) @@ -122,7 +123,7 @@ private fun CryptoBalance( ) is TokenDetailsBalanceBlockState.Content -> Text( modifier = modifier, - text = if (isBalanceHidden) STARS else state.cryptoBalance, + text = if (isBalanceHidden) STARS else state.displayCryptoBalance, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) @@ -171,7 +172,7 @@ private fun Preview_TokenDetailsBalanceBlock( @PreviewParameter(TokenDetailsBalanceBlockStateProvider::class) state: TokenDetailsBalanceBlockState, ) { TangemThemePreview { - TokenDetailsBalanceBlock(state = state, isBalanceHidden = false) + TokenDetailsBalanceBlock(state = state, isBalanceHidden = false, isStakingAvailable = true) } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/StakingBalanceBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/StakingBalanceBlock.kt index 4a2929a40d..34986f925b 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/StakingBalanceBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/StakingBalanceBlock.kt @@ -2,10 +2,14 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components. import android.content.res.Configuration import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* +import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -30,6 +34,11 @@ fun StakingBalanceBlock(state: StakingBalance.Content, modifier: Modifier = Modi .fillMaxWidth() .clip(TangemTheme.shapes.roundedCornersXMedium) .background(TangemTheme.colors.background.action) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberRipple(), + onClick = state.onStakeClicked, + ) .padding(TangemTheme.dimens.spacing12), ) { Column( @@ -44,7 +53,7 @@ fun StakingBalanceBlock(state: StakingBalance.Content, modifier: Modifier = Modi ) Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { Text( - text = state.fiatAmount.resolveReference(), + text = state.fiatValue.resolveReference(), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.primary1, ) @@ -54,13 +63,13 @@ fun StakingBalanceBlock(state: StakingBalance.Content, modifier: Modifier = Modi color = TangemTheme.colors.text.primary1, ) Text( - text = state.cryptoAmount.resolveReference(), + text = state.cryptoValue.resolveReference(), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.tertiary, ) } Text( - text = state.rewardAmount.resolveReference(), + text = state.rewardValue.resolveReference(), style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 64dba1df48..848518dbf1 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -30,6 +30,7 @@ import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase import com.tangem.domain.staking.GetStakingAvailabilityUseCase import com.tangem.domain.staking.GetStakingEntryInfoUseCase +import com.tangem.domain.staking.GetStakingYieldBalanceUseCase import com.tangem.domain.staking.GetYieldUseCase import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.tokens.* @@ -107,6 +108,7 @@ internal class TokenDetailsViewModel @Inject constructor( private val stakingFeatureToggles: StakingFeatureToggles, private val getStakingAvailabilityUseCase: GetStakingAvailabilityUseCase, private val getYieldUseCase: GetYieldUseCase, + private val getStakingYieldBalanceUseCase: GetStakingYieldBalanceUseCase, private val swapRepository: SwapRepository, private val swapTransactionRepository: SwapTransactionRepository, private val quotesRepository: QuotesRepository, @@ -133,6 +135,7 @@ internal class TokenDetailsViewModel @Inject constructor( ?: error("This screen can't open without `CryptoCurrency`") private val userWallet: UserWallet + private var stakingIntegrationId: String? = null lateinit var router: InnerTokenDetailsRouter @@ -149,11 +152,11 @@ internal class TokenDetailsViewModel @Inject constructor( private val stateFactory = TokenDetailsStateFactory( currentStateProvider = Provider { uiState.value }, appCurrencyProvider = Provider(selectedAppCurrencyFlow::value), + cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, clickIntents = this, symbol = cryptoCurrency.symbol, decimals = cryptoCurrency.decimals, featureToggles = tokenDetailsFeatureToggles, - isStakingEnabled = stakingFeatureToggles.isStakingEnabled, ) private val exchangeStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) { @@ -217,7 +220,6 @@ internal class TokenDetailsViewModel @Inject constructor( } private fun updateContent() { - subscribeOnCurrencyStatusUpdates() subscribeOnExchangeTransactionsUpdates() updateTxHistory(refresh = false, showItemsLoading = true) @@ -279,12 +281,36 @@ internal class TokenDetailsViewModel @Inject constructor( ) .distinctUntilChanged() .onEach { maybeCurrencyStatus -> - internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus) - maybeCurrencyStatus.onRight { status -> - cryptoCurrencyStatus = status - updateButtons(currencyStatus = status) - updateWarnings(status) - } + maybeCurrencyStatus.fold( + ifLeft = { + internalUiState.value = stateFactory.getCurrencyLoadedBalanceState( + maybeCurrencyStatus, + null, + ) + }, + ifRight = { status -> + cryptoCurrencyStatus = status + + if (stakingIntegrationId != null) { + getStakingYieldBalanceUseCase( + userWalletId = userWalletId, + address = status.value.networkAddress?.defaultAddress?.value.orEmpty(), + integrationId = stakingIntegrationId.orEmpty(), + ).onEach { maybeYieldBalance -> + internalUiState.value = stateFactory.getCurrencyLoadedBalanceState( + maybeCurrencyStatus, + maybeYieldBalance, + ) + }.launchIn(viewModelScope) + } else { + internalUiState.value = + stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus, null) + } + + updateButtons(currencyStatus = status) + updateWarnings(status) + }, + ) currencyStatusAnalyticsSender.send(maybeCurrencyStatus) } .flowOn(dispatchers.main) @@ -385,8 +411,12 @@ internal class TokenDetailsViewModel @Inject constructor( ) internalUiState.value = stateFactory.getStateWithUpdatedStakingAvailability(stakingAvailability) if (stakingAvailability is StakingAvailability.Available) { + stakingIntegrationId = stakingAvailability.integrationId val stakingInfo = getStakingEntryInfoUseCase(stakingAvailability.integrationId) + subscribeOnCurrencyStatusUpdates() internalUiState.value = stateFactory.getStateWithStaking(stakingInfo) + } else { + subscribeOnCurrencyStatusUpdates() } } }