Updated on 2026-08-14
This commit is contained in:
commit
e0262f6d9f
19 changed files with 420 additions and 121 deletions
|
|
@ -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<List<YieldBalance>> = channelFlow {
|
||||
): Flow<YieldBalance> = 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<NetworkStatus>,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
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<NetworkStatus>,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
integrationId: String,
|
||||
): Flow<List<YieldBalanceList>> = channelFlow {
|
||||
): Flow<YieldBalanceList> = 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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<BalanceDTO, YieldBalance> {
|
||||
override fun convert(value: BalanceDTO): YieldBalance {
|
||||
return YieldBalance(
|
||||
type = BalanceType.valueOf(value.type.name),
|
||||
amount = value.amount,
|
||||
pricePerShare = value.pricePerShare,
|
||||
)
|
||||
internal class YieldBalanceConverter : Converter<YieldBalanceConverter.Data, YieldBalance> {
|
||||
|
||||
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<BalanceDTO>,
|
||||
val integrationId: String?,
|
||||
)
|
||||
}
|
||||
|
|
@ -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<YieldBalanceWrapperDTO, YieldBalanceList> {
|
||||
internal class YieldBalanceListConverter : Converter<List<YieldBalanceWrapperDTO>, 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<YieldBalanceWrapperDTO>): YieldBalanceList {
|
||||
return if (value.isEmpty()) {
|
||||
YieldBalanceList.Empty
|
||||
} else {
|
||||
YieldBalanceList.Data(
|
||||
balances = value.map {
|
||||
converter.convert(
|
||||
YieldBalanceConverter.Data(
|
||||
balance = it.balances,
|
||||
integrationId = it.integrationId,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BalanceItem>,
|
||||
val integrationId: String?,
|
||||
)
|
||||
|
||||
data class BalanceItem(
|
||||
val type: BalanceType,
|
||||
val amount: BigDecimal,
|
||||
val pricePerShare: BigDecimal,
|
||||
val rawCurrencyId: String?,
|
||||
)
|
||||
|
||||
enum class BalanceType {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,17 @@
|
|||
package com.tangem.domain.staking.model
|
||||
|
||||
data class YieldBalanceList(
|
||||
val balances: List<YieldBalance>,
|
||||
)
|
||||
sealed class YieldBalanceList {
|
||||
|
||||
data class Data(
|
||||
val balances: List<YieldBalance>,
|
||||
) : 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()
|
||||
}
|
||||
|
|
@ -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<Throwable, Unit> = Either.catch {
|
||||
stakingRepository.fetchSingleYieldBalance(
|
||||
userWalletId = userWalletId,
|
||||
networkStatus = networkStatus,
|
||||
address = address,
|
||||
integrationId = integrationId,
|
||||
refresh = refresh,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<Throwable, List<YieldBalance>> {
|
||||
): EitherFlow<Throwable, YieldBalance> {
|
||||
return stakingRepository.getSingleYieldBalanceFlow(
|
||||
userWalletId = userWalletId,
|
||||
networkStatus = networkStatus,
|
||||
address = address,
|
||||
integrationId = integrationId,
|
||||
).map<List<YieldBalance>, Either<Throwable, List<YieldBalance>>> { it.right() }
|
||||
).map<YieldBalance, Either<Throwable, YieldBalance>> { it.right() }
|
||||
.catch { emit(it.left()) }
|
||||
}
|
||||
}
|
||||
|
|
@ -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<List<YieldBalance>>
|
||||
): Flow<YieldBalance>
|
||||
|
||||
suspend fun fetchMultiYieldBalance(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<NetworkStatus>,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
integrationId: String,
|
||||
refresh: Boolean = false,
|
||||
)
|
||||
|
||||
fun getMultiYieldBalanceFlow(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<NetworkStatus>,
|
||||
addresses: List<CryptoCurrencyAddress>,
|
||||
integrationId: String,
|
||||
): Flow<List<YieldBalanceList>>
|
||||
): Flow<YieldBalanceList>
|
||||
}
|
||||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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<TokenDetailsActionButton>,
|
||||
override val balanceSegmentedButtonConfig: ImmutableList<TokenBalanceSegmentedButtonConfig>,
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -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<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||
) : Converter<TokenBalanceSegmentedButtonConfig, TokenDetailsState> {
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val isStakingEnabled: Boolean,
|
||||
private val symbol: String,
|
||||
private val decimals: Int,
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
) : Converter<Either<CurrencyStatusError, CryptoCurrencyStatus>, TokenDetailsState> {
|
||||
) : Converter<TokenDetailsLoadedBalanceConverter.Data, TokenDetailsState> {
|
||||
|
||||
private val txHistoryItemConverter by lazy {
|
||||
TokenDetailsTxHistoryTransactionStateConverter(symbol, decimals, clickIntents)
|
||||
}
|
||||
|
||||
override fun convert(value: Either<CurrencyStatusError, CryptoCurrencyStatus>): TokenDetailsState {
|
||||
return value.fold(ifLeft = { convertError() }, ifRight = ::convert)
|
||||
data class Data(
|
||||
val maybeCryptoCurrencyStatus: Either<CurrencyStatusError, CryptoCurrencyStatus>,
|
||||
val maybeYieldBalance: Either<Throwable, YieldBalance>?,
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||
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<CurrencyStatusError, CryptoCurrencyStatus>,
|
||||
yieldBalanceEither: Either<Throwable, YieldBalance>?,
|
||||
): TokenDetailsState {
|
||||
return tokenDetailsLoadedBalanceConverter.convert(cryptoCurrencyEither)
|
||||
return tokenDetailsLoadedBalanceConverter.convert(
|
||||
TokenDetailsLoadedBalanceConverter.Data(cryptoCurrencyEither, yieldBalanceEither),
|
||||
)
|
||||
}
|
||||
|
||||
fun getManageButtonsState(actions: List<TokenActionsState.ActionState>): 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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue