Updated on 2026-08-14
This commit is contained in:
commit
aed332c720
44 changed files with 514 additions and 351 deletions
|
|
@ -224,4 +224,12 @@ internal object StakingDomainModule {
|
|||
): CheckAccountInitializedUseCase {
|
||||
return CheckAccountInitializedUseCase(walletManagersFacade)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetActionRequirementAmountUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
): GetActionRequirementAmountUseCase {
|
||||
return GetActionRequirementAmountUseCase(stakingRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,6 @@ internal class DefaultCardSettingsComponent @AssistedInject constructor(
|
|||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.screenState.collectAsStateWithLifecycle()
|
||||
|
||||
CardSettingsScreen(modifier = modifier, state = state)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ internal class CardSettingsModel @Inject constructor(
|
|||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
// Reset card scanned data
|
||||
cardSettingsInteractor.clear()
|
||||
// Restore the previous value of access code request policy
|
||||
cardSdkConfigRepository.isBiometricsRequestPolicy = previousBiometricsRequestPolicy
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ data class OnrampQuoteResponse(
|
|||
val providerId: String,
|
||||
|
||||
@Json(name = "minFromAmount")
|
||||
val minFromAmount: String,
|
||||
val minFromAmount: String?,
|
||||
|
||||
@Json(name = "maxFromAmount")
|
||||
val maxFromAmount: String,
|
||||
val maxFromAmount: String?,
|
||||
|
||||
@Json(name = "minToAmount")
|
||||
val minToAmount: String?,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ import kotlinx.coroutines.async
|
|||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.joda.time.DateTime
|
||||
import timber.log.Timber
|
||||
|
|
@ -303,8 +302,12 @@ internal class DefaultOnrampRepository(
|
|||
OnrampQuote.Data(
|
||||
fromAmount = fromOnrampAmount,
|
||||
toAmount = convertToAmount(response.toAmount, cryptoCurrency),
|
||||
minFromAmount = convertToAmount(response.minFromAmount, cryptoCurrency),
|
||||
maxFromAmount = convertToAmount(response.maxFromAmount, cryptoCurrency),
|
||||
minFromAmount = response.minFromAmount?.let {
|
||||
convertToAmount(it, cryptoCurrency)
|
||||
},
|
||||
maxFromAmount = response.maxFromAmount?.let {
|
||||
convertToAmount(it, cryptoCurrency)
|
||||
},
|
||||
paymentMethod = paymentMethod,
|
||||
provider = provider,
|
||||
countryCode = response.countryCode,
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
||||
|
|
@ -119,7 +120,7 @@ internal class DefaultStakingRepository(
|
|||
when (val stakingTokensWithYields = stakeKitApi.getEnabledYields(preferredValidatorsOnly = false)) {
|
||||
is ApiResponse.Success -> stakingYieldsStore.store(
|
||||
stakingTokensWithYields.data.data.filter {
|
||||
it.isAvailable ?: false
|
||||
it.isAvailable == true
|
||||
},
|
||||
)
|
||||
else -> {
|
||||
|
|
@ -650,15 +651,24 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
|
||||
override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean {
|
||||
return withContext(dispatchers.io) {
|
||||
stakingBalanceStore.getSyncOrNull(userWalletId)
|
||||
?.let {
|
||||
it.isNotEmpty() &&
|
||||
it.any { yieldBalance ->
|
||||
(yieldBalance as? YieldBalance.Data)?.balance?.items?.isNotEmpty() == true
|
||||
}
|
||||
return withContext(dispatchers.default) {
|
||||
val balances = stakingBalanceStoreV2.getAllSyncOrNull(userWalletId) ?: return@withContext false
|
||||
|
||||
val hasDataYieldBalance by lazy {
|
||||
balances.any { yieldBalance ->
|
||||
(yieldBalance as? YieldBalance.Data)?.balance?.items?.isNotEmpty() == true
|
||||
}
|
||||
?: false
|
||||
}
|
||||
|
||||
balances.isNotEmpty() && hasDataYieldBalance
|
||||
}
|
||||
}
|
||||
|
||||
override fun getActionRequirementAmount(integrationId: String, stakingActionType: StakingActionType): BigDecimal? {
|
||||
return when {
|
||||
stakingIdFactory.isPolygonIntegrationId(integrationId) &&
|
||||
stakingActionType == StakingActionType.CLAIM_REWARDS -> BigDecimal.ONE
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.data.staking.single
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.data.staking.utils.StakingIdFactory
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
|
|
@ -7,6 +9,7 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceProducer
|
|||
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
|
||||
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.indexOfFirstOrNull
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -14,6 +17,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Default implementation of [SingleYieldBalanceProducer]
|
||||
|
|
@ -29,6 +33,7 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
|
|||
@Assisted private val params: SingleYieldBalanceProducer.Params,
|
||||
private val multiYieldBalanceSupplier: MultiYieldBalanceSupplier,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : SingleYieldBalanceProducer {
|
||||
|
||||
|
|
@ -48,8 +53,34 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor(
|
|||
.mapNotNull { balances ->
|
||||
val currentStakingId = getStakingId() ?: return@mapNotNull YieldBalance.Unsupported
|
||||
|
||||
balances.firstOrNull { it.getStakingId() == currentStakingId }
|
||||
?: YieldBalance.Unsupported
|
||||
val currentBalances = balances.filter { it.getStakingId() == currentStakingId }
|
||||
|
||||
if (currentBalances.size > 1) {
|
||||
analyticsExceptionHandler.sendException(
|
||||
event = ExceptionAnalyticsEvent(
|
||||
exception = IllegalStateException("Multiple balances found for staking ID"),
|
||||
params = mapOf(
|
||||
"stakingId" to currentStakingId.toString(),
|
||||
"balances" to currentBalances.joinToString(",") { it.toString() },
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
Timber.w(
|
||||
"Multiple balances found for staking ID $currentStakingId:\n%s",
|
||||
currentBalances.joinToString("\n"),
|
||||
)
|
||||
|
||||
val dataIndex = currentBalances.indexOfFirstOrNull { it is YieldBalance.Data }
|
||||
|
||||
if (dataIndex != null) {
|
||||
currentBalances[dataIndex]
|
||||
} else {
|
||||
currentBalances.first()
|
||||
}
|
||||
} else {
|
||||
currentBalances.firstOrNull() ?: YieldBalance.Unsupported
|
||||
}
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.flowOn(dispatchers.default)
|
||||
|
|
|
|||
|
|
@ -109,8 +109,15 @@ internal class DefaultYieldsBalancesStore(
|
|||
private suspend fun storeInPersistence(userWalletId: UserWalletId, values: Set<YieldBalanceWrapperDTO>) {
|
||||
persistenceStore.updateData { current ->
|
||||
current.toMutableMap().apply {
|
||||
this[userWalletId.stringValue] = current[userWalletId.stringValue]
|
||||
?.addOrReplace(items = values) { old, new -> old.getStakingId() == new.getStakingId() }
|
||||
this[userWalletId.stringValue] = this[userWalletId.stringValue]
|
||||
?.addOrReplace(items = values) { old, new ->
|
||||
val oldId = old.getStakingId()
|
||||
val newId = new.getStakingId()
|
||||
|
||||
if (oldId == null || newId == null) return@addOrReplace false
|
||||
|
||||
oldId == newId
|
||||
}
|
||||
?: values
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ internal class StakingIdFactory @Inject constructor(
|
|||
return integrationIdMap[integrationKey]
|
||||
}
|
||||
|
||||
fun isPolygonIntegrationId(integrationId: String): Boolean = integrationId == ETHEREUM_POLYGON_INTEGRATION_ID
|
||||
|
||||
@Suppress("UnusedPrivateMember", "unused")
|
||||
companion object {
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.common.test.utils.getEmittedValues
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.data.staking.toDomain
|
||||
import com.tangem.data.staking.utils.StakingIdFactory
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
|
|
@ -32,12 +33,14 @@ internal class DefaultSingleYieldBalanceProducerTest {
|
|||
|
||||
private val multiNetworkStatusSupplier = mockk<MultiYieldBalanceSupplier>()
|
||||
private val stakingIdFactory = mockk<StakingIdFactory>()
|
||||
private val analyticsExceptionHandler = mockk<AnalyticsExceptionHandler>(relaxUnitFun = true)
|
||||
private val dispatchers = TestingCoroutineDispatcherProvider()
|
||||
|
||||
private val producer = DefaultSingleYieldBalanceProducer(
|
||||
params = params,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
multiYieldBalanceSupplier = multiNetworkStatusSupplier,
|
||||
analyticsExceptionHandler = analyticsExceptionHandler,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -97,31 +97,35 @@ internal class DefaultCurrenciesRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun addCurrencies(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
withContext(dispatchers.io) {
|
||||
val savedCurrencies = requireNotNull(
|
||||
value = getSavedUserTokensResponseSync(key = userWalletId),
|
||||
lazyMessage = { "Saved tokens empty. Can not perform add currencies action" },
|
||||
)
|
||||
override suspend fun addCurrencies(
|
||||
userWalletId: UserWalletId,
|
||||
currencies: List<CryptoCurrency>,
|
||||
): List<CryptoCurrency> = withContext(dispatchers.io) {
|
||||
val savedCurrencies = requireNotNull(
|
||||
value = getSavedUserTokensResponseSync(key = userWalletId),
|
||||
lazyMessage = { "Saved tokens empty. Can not perform add currencies action" },
|
||||
)
|
||||
|
||||
val currenciesToAdd = populateCurrenciesWithMissedCoins(
|
||||
currencies = currencies,
|
||||
).let {
|
||||
filterAlreadyAddedCurrencies(savedCurrencies.tokens, it)
|
||||
}
|
||||
val updatedResponse = savedCurrencies.copy(
|
||||
tokens = savedCurrencies.tokens + currenciesToAdd.map(userTokensResponseFactory::createResponseToken),
|
||||
)
|
||||
userTokensSaver.storeAndPush(
|
||||
userWalletId = userWalletId,
|
||||
response = updatedResponse,
|
||||
)
|
||||
val currenciesToAdd = filterAlreadyAddedCurrencies(
|
||||
savedCurrencies = savedCurrencies.tokens,
|
||||
currenciesToAdd = populateCurrenciesWithMissedCoins(currencies = currencies),
|
||||
)
|
||||
|
||||
fetchExpressAssetsByNetworkIds(
|
||||
userWallet = userWalletsStore.getSyncStrict(key = userWalletId),
|
||||
userTokens = updatedResponse,
|
||||
)
|
||||
}
|
||||
val updatedResponse = savedCurrencies.copy(
|
||||
tokens = savedCurrencies.tokens + currenciesToAdd.map(userTokensResponseFactory::createResponseToken),
|
||||
)
|
||||
|
||||
userTokensSaver.storeAndPush(
|
||||
userWalletId = userWalletId,
|
||||
response = updatedResponse,
|
||||
)
|
||||
|
||||
fetchExpressAssetsByNetworkIds(
|
||||
userWallet = userWalletsStore.getSyncStrict(key = userWalletId),
|
||||
userTokens = updatedResponse,
|
||||
)
|
||||
|
||||
currenciesToAdd
|
||||
}
|
||||
|
||||
private fun filterAlreadyAddedCurrencies(
|
||||
|
|
@ -573,7 +577,7 @@ internal class DefaultCurrenciesRepository(
|
|||
|
||||
override fun isNetworkFeeZero(userWalletId: UserWalletId, network: Network): Boolean {
|
||||
val blockchain = Blockchain.fromNetworkId(network.backendId)
|
||||
return blockchain?.isNetworkFeeZero() ?: false
|
||||
return blockchain?.isNetworkFeeZero() == true
|
||||
}
|
||||
|
||||
override suspend fun syncTokens(userWalletId: UserWalletId) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.card
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.right
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
|
|
@ -22,16 +23,17 @@ class GetExtendedPublicKeyForCurrencyUseCase(
|
|||
private val derivationsRepository: DerivationsRepository,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either<Throwable, String> {
|
||||
return Either.catch {
|
||||
val userWallet = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
?: error("Wallet not found")
|
||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
?: error("Wallet not found for userWalletId=$userWalletId and network=$network")
|
||||
|
||||
val blockchain = network.toBlockchain()
|
||||
val isSecp256k1Blockchain = Blockchain.secp256k1Blockchains(network.isTestnet).contains(blockchain)
|
||||
|
||||
val hdKey = if (isSecp256k1Blockchain) {
|
||||
userWallet.wallet.publicKey.derivationType?.hdKey ?: error("No derivation found")
|
||||
walletManager.wallet.publicKey.derivationType?.hdKey ?: error("No derivation found")
|
||||
} else {
|
||||
error("No derivation found")
|
||||
}
|
||||
|
|
@ -50,7 +52,7 @@ class GetExtendedPublicKeyForCurrencyUseCase(
|
|||
val pendingDerivations = getPendingDerivations(childKey, parentKey)
|
||||
val derivedKeys = deriveKeys(
|
||||
userWalletId = userWalletId,
|
||||
seedKey = userWallet.wallet.publicKey.seedKey,
|
||||
seedKey = walletManager.wallet.publicKey.seedKey,
|
||||
paths = pendingDerivations,
|
||||
)
|
||||
|
||||
|
|
@ -73,15 +75,17 @@ class GetExtendedPublicKeyForCurrencyUseCase(
|
|||
/**
|
||||
* @return true if xpub generation is supported, false otherwise
|
||||
*/
|
||||
suspend fun isSupported(userWalletId: UserWalletId, network: Network): Boolean {
|
||||
val userWallet = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
?: error("Wallet not found")
|
||||
suspend fun isSupported(userWalletId: UserWalletId, network: Network): Either<Throwable, Boolean> = Either.catch {
|
||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, network)
|
||||
?: error("Wallet not found for user wallet $userWalletId and network ${network.id}")
|
||||
|
||||
val blockchain = network.toBlockchain()
|
||||
val isSecp256k1Blockchain = Blockchain.secp256k1Blockchains(network.isTestnet).contains(blockchain)
|
||||
val isHdKey = userWallet.wallet.publicKey.derivationType?.hdKey
|
||||
val isHdKey = walletManager.wallet.publicKey.derivationType?.hdKey
|
||||
|
||||
return isSecp256k1Blockchain && isHdKey != null
|
||||
val isSupported = isSecp256k1Blockchain && isHdKey != null
|
||||
|
||||
return isSupported.right()
|
||||
}
|
||||
|
||||
private suspend fun deriveKeys(
|
||||
|
|
|
|||
|
|
@ -41,16 +41,15 @@ class SaveMarketTokensUseCase(
|
|||
removedNetworks: Set<TokenMarketInfo.Network>,
|
||||
): Either<Throwable, Unit> = Either.catch {
|
||||
if (removedNetworks.isNotEmpty()) {
|
||||
currenciesRepository.removeCurrencies(
|
||||
userWalletId = userWalletId,
|
||||
currencies = removedNetworks.mapNotNull {
|
||||
marketsTokenRepository.createCryptoCurrency(
|
||||
userWalletId = userWalletId,
|
||||
token = tokenMarketParams,
|
||||
network = it,
|
||||
)
|
||||
},
|
||||
)
|
||||
val removedCurrencies = removedNetworks.mapNotNull {
|
||||
marketsTokenRepository.createCryptoCurrency(
|
||||
userWalletId = userWalletId,
|
||||
token = tokenMarketParams,
|
||||
network = it,
|
||||
)
|
||||
}
|
||||
|
||||
currenciesRepository.removeCurrencies(userWalletId = userWalletId, currencies = removedCurrencies)
|
||||
}
|
||||
|
||||
if (addedNetworks.isNotEmpty()) {
|
||||
|
|
@ -67,13 +66,16 @@ class SaveMarketTokensUseCase(
|
|||
)
|
||||
}
|
||||
|
||||
currenciesRepository.addCurrencies(userWalletId = userWalletId, currencies = addedCurrencies)
|
||||
val savedCurrencies = currenciesRepository.addCurrencies(
|
||||
userWalletId = userWalletId,
|
||||
currencies = addedCurrencies,
|
||||
)
|
||||
|
||||
refreshUpdatedNetworks(userWalletId, addedCurrencies)
|
||||
refreshUpdatedNetworks(userWalletId, savedCurrencies)
|
||||
|
||||
refreshUpdatedYieldBalances(userWalletId, addedCurrencies)
|
||||
refreshUpdatedYieldBalances(userWalletId, savedCurrencies)
|
||||
|
||||
refreshUpdatedQuotes(addedCurrencies)
|
||||
refreshUpdatedQuotes(savedCurrencies)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ sealed class OnrampQuote {
|
|||
override val fromAmount: OnrampAmount,
|
||||
override val countryCode: String,
|
||||
val toAmount: OnrampAmount,
|
||||
val minFromAmount: OnrampAmount,
|
||||
val maxFromAmount: OnrampAmount,
|
||||
val minFromAmount: OnrampAmount?,
|
||||
val maxFromAmount: OnrampAmount?,
|
||||
) : OnrampQuote()
|
||||
|
||||
data class AmountError(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import java.math.BigDecimal
|
||||
|
||||
class GetActionRequirementAmountUseCase(
|
||||
private val stakingRepository: StakingRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(integrationId: String, actionType: StakingActionType): Either<Throwable, BigDecimal?> =
|
||||
Either.catch {
|
||||
stakingRepository.getActionRequirementAmount(integrationId, actionType)
|
||||
}
|
||||
}
|
||||
|
|
@ -14,11 +14,13 @@ import com.tangem.domain.staking.model.stakekit.YieldBalance
|
|||
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
interface StakingRepository {
|
||||
|
|
@ -100,4 +102,9 @@ interface StakingRepository {
|
|||
fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval
|
||||
|
||||
suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean
|
||||
|
||||
/**
|
||||
* Return action requirement amount
|
||||
*/
|
||||
fun getActionRequirementAmount(integrationId: String, stakingActionType: StakingActionType): BigDecimal?
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ interface CurrenciesRepository {
|
|||
* @throws DataError.UserWalletError.WrongUserWallet If single-currency user wallet
|
||||
* ID provided.
|
||||
*/
|
||||
suspend fun addCurrencies(userWalletId: UserWalletId, currencies: List<CryptoCurrency>)
|
||||
suspend fun addCurrencies(userWalletId: UserWalletId, currencies: List<CryptoCurrency>): List<CryptoCurrency>
|
||||
|
||||
/**
|
||||
* Removes currency from a specific user wallet.
|
||||
|
|
|
|||
|
|
@ -48,7 +48,10 @@ internal class MockCurrenciesRepository(
|
|||
|
||||
override suspend fun saveNewCurrenciesList(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) = Unit
|
||||
|
||||
override suspend fun addCurrencies(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) = Unit
|
||||
override suspend fun addCurrencies(
|
||||
userWalletId: UserWalletId,
|
||||
currencies: List<CryptoCurrency>,
|
||||
): List<CryptoCurrency> = emptyList()
|
||||
|
||||
override suspend fun removeCurrency(userWalletId: UserWalletId, currency: CryptoCurrency) {
|
||||
removeCurrencyResult.onLeft { throw it }
|
||||
|
|
|
|||
|
|
@ -188,6 +188,8 @@ class MockStakingRepository : StakingRepository {
|
|||
override fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval = StakingApproval.Empty
|
||||
|
||||
override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean = false
|
||||
override fun getActionRequirementAmount(integrationId: String, stakingActionType: StakingActionType): BigDecimal? =
|
||||
null
|
||||
|
||||
private companion object {
|
||||
val yield = Yield(
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.features.nft.collections.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
|
|
@ -17,31 +17,28 @@ import com.tangem.features.nft.impl.R
|
|||
internal fun NFTCollections(state: NFTCollectionsStateUM, modifier: Modifier = Modifier) {
|
||||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
topBar = {
|
||||
AppBarWithBackButton(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
onBackClick = state.onBackClick,
|
||||
text = stringResourceSafe(id = R.string.nft_collections_title),
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
)
|
||||
},
|
||||
content = { innerPadding ->
|
||||
TangemPullToRefreshContainer(
|
||||
config = state.pullToRefreshConfig,
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
when (val content = state.content) {
|
||||
is NFTCollectionsUM.Content -> NFTCollectionsContent(content)
|
||||
is NFTCollectionsUM.Empty -> NFTCollectionsEmpty(content)
|
||||
is NFTCollectionsUM.Failed -> NFTCollectionsFailed(content)
|
||||
is NFTCollectionsUM.Loading -> NFTCollectionsLoading(content)
|
||||
}
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.secondary),
|
||||
) {
|
||||
AppBarWithBackButton(
|
||||
modifier = Modifier,
|
||||
onBackClick = state.onBackClick,
|
||||
text = stringResourceSafe(id = R.string.nft_collections_title),
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
)
|
||||
|
||||
TangemPullToRefreshContainer(
|
||||
config = state.pullToRefreshConfig,
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
when (val content = state.content) {
|
||||
is NFTCollectionsUM.Content -> NFTCollectionsContent(content)
|
||||
is NFTCollectionsUM.Empty -> NFTCollectionsEmpty(content)
|
||||
is NFTCollectionsUM.Failed -> NFTCollectionsFailed(content)
|
||||
is NFTCollectionsUM.Loading -> NFTCollectionsLoading(content)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,14 +52,14 @@ internal fun NFTCollectionsEmpty(state: NFTCollectionsUM.Empty, modifier: Modifi
|
|||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing48)
|
||||
.widthIn(min = TangemTheme.dimens.size158),
|
||||
text = stringResourceSafe(R.string.nft_collections_receive),
|
||||
onClick = state.onReceiveClick,
|
||||
)
|
||||
}
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.BottomCenter),
|
||||
text = stringResourceSafe(R.string.nft_collections_receive),
|
||||
onClick = state.onReceiveClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ import com.tangem.features.nft.common.NFTRoute
|
|||
@Composable
|
||||
internal fun NFTContent(stackState: ChildStack<NFTRoute, ComposableContentComponent>) {
|
||||
Column(
|
||||
modifier = Modifier.Companion
|
||||
.background(color = TangemTheme.colors.background.tertiary)
|
||||
modifier = Modifier
|
||||
.background(color = TangemTheme.colors.background.secondary)
|
||||
.fillMaxSize()
|
||||
.imePadding()
|
||||
.systemBarsPadding(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.nft.details.entity.transformer
|
||||
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
|
|
@ -8,6 +9,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.nft.models.NFTSalePrice
|
||||
import com.tangem.features.nft.details.entity.NFTAssetUM
|
||||
import com.tangem.features.nft.details.entity.NFTDetailsUM
|
||||
import com.tangem.features.nft.impl.R
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class NFTPriceChangeTransformer(
|
||||
|
|
@ -18,35 +20,50 @@ internal class NFTPriceChangeTransformer(
|
|||
override fun transform(prevState: NFTDetailsUM): NFTDetailsUM {
|
||||
val topInfo = prevState.nftAsset.topInfo as? NFTAssetUM.TopInfo.Content ?: return prevState
|
||||
|
||||
return prevState.copy(
|
||||
nftAsset = prevState.nftAsset.copy(
|
||||
topInfo = topInfo.copy(
|
||||
salePrice = when (nftSalePrice) {
|
||||
is NFTSalePrice.Empty,
|
||||
is NFTSalePrice.Error,
|
||||
-> NFTAssetUM.SalePrice.Empty
|
||||
is NFTSalePrice.Loading -> NFTAssetUM.SalePrice.Loading
|
||||
is NFTSalePrice.Value -> NFTAssetUM.SalePrice.Content(
|
||||
isFlickering = false,
|
||||
cryptoPrice = stringReference(
|
||||
nftSalePrice.value.format {
|
||||
crypto(
|
||||
symbol = nftSalePrice.symbol,
|
||||
decimals = nftSalePrice.decimals,
|
||||
)
|
||||
},
|
||||
),
|
||||
fiatPrice = stringReference(
|
||||
nftSalePrice.fiatValue.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
},
|
||||
),
|
||||
val salePrice = when (nftSalePrice) {
|
||||
is NFTSalePrice.Empty,
|
||||
is NFTSalePrice.Error,
|
||||
-> NFTAssetUM.SalePrice.Empty
|
||||
is NFTSalePrice.Loading -> NFTAssetUM.SalePrice.Loading
|
||||
is NFTSalePrice.Value -> NFTAssetUM.SalePrice.Content(
|
||||
isFlickering = false,
|
||||
cryptoPrice = stringReference(
|
||||
nftSalePrice.value.format {
|
||||
crypto(
|
||||
symbol = nftSalePrice.symbol,
|
||||
decimals = nftSalePrice.decimals,
|
||||
)
|
||||
},
|
||||
),
|
||||
fiatPrice = stringReference(
|
||||
nftSalePrice.fiatValue.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val hasSalePrice = salePrice !is NFTAssetUM.SalePrice.Empty
|
||||
|
||||
val newTopInfo = if (
|
||||
topInfo.rarity is NFTAssetUM.Rarity.Empty &&
|
||||
topInfo.description.isNullOrEmpty() &&
|
||||
!hasSalePrice
|
||||
) {
|
||||
NFTAssetUM.TopInfo.Empty
|
||||
} else {
|
||||
topInfo.copy(
|
||||
title = resourceReference(R.string.nft_details_last_sale_price).takeIf { hasSalePrice },
|
||||
salePrice = salePrice,
|
||||
)
|
||||
}
|
||||
|
||||
return prevState.copy(
|
||||
nftAsset = prevState.nftAsset.copy(
|
||||
topInfo = newTopInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
package com.tangem.features.nft.details.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.FabPosition
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
|
|
@ -22,25 +19,25 @@ import com.tangem.features.nft.impl.R
|
|||
internal fun NFTDetails(state: NFTDetailsUM, modifier: Modifier = Modifier) {
|
||||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
topBar = {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.secondary),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
TangemTopAppBar(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
modifier = Modifier,
|
||||
startButton = TopAppBarButtonUM(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onIconClicked = state.onBackClick,
|
||||
),
|
||||
title = state.nftAsset.name,
|
||||
)
|
||||
},
|
||||
content = { innerPadding ->
|
||||
|
||||
TangemPullToRefreshContainer(
|
||||
config = state.pullToRefreshConfig,
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize(),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
NFTDetailsAsset(
|
||||
state = state.nftAsset,
|
||||
|
|
@ -49,16 +46,19 @@ internal fun NFTDetails(state: NFTDetailsUM, modifier: Modifier = Modifier) {
|
|||
onExploreClick = state.onExploreClick,
|
||||
)
|
||||
}
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.Center,
|
||||
floatingActionButton = {
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
text = stringResourceSafe(id = R.string.common_send),
|
||||
onClick = state.onSendClick,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
text = stringResourceSafe(id = R.string.common_send),
|
||||
onClick = state.onSendClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -173,8 +173,7 @@ internal fun NFTDetailsGroupBlock(
|
|||
} else {
|
||||
onBlockClick?.invoke()
|
||||
}
|
||||
}
|
||||
.padding(top = TangemTheme.dimens.spacing4),
|
||||
},
|
||||
text = value.resolveReference(),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.features.nft.receive.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
|
||||
|
|
@ -22,41 +22,33 @@ import com.tangem.features.nft.receive.entity.NFTReceiveUM
|
|||
internal fun NFTReceive(state: NFTReceiveUM, modifier: Modifier = Modifier) {
|
||||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
topBar = {
|
||||
TangemTopAppBar(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
startButton = TopAppBarButtonUM(
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
onIconClicked = state.onBackClick,
|
||||
),
|
||||
title = stringResourceSafe(id = R.string.nft_receive_title),
|
||||
subtitle = state.appBarSubtitle.resolveReference(),
|
||||
)
|
||||
},
|
||||
content = { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
SearchBar(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
state = state.search,
|
||||
colors = TangemSearchBarDefaults.secondaryTextFieldColors,
|
||||
)
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.secondary),
|
||||
) {
|
||||
TangemTopAppBar(
|
||||
modifier = Modifier,
|
||||
startButton = TopAppBarButtonUM(
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
onIconClicked = state.onBackClick,
|
||||
),
|
||||
title = stringResourceSafe(id = R.string.nft_receive_title),
|
||||
subtitle = state.appBarSubtitle.resolveReference(),
|
||||
)
|
||||
|
||||
when (val networks = state.networks) {
|
||||
is NFTReceiveUM.Networks.Content -> NFTReceiveNetworksContent(networks)
|
||||
is NFTReceiveUM.Networks.Empty -> NFTReceiveNetworksEmpty()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
SearchBar(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
state = state.search,
|
||||
colors = TangemSearchBarDefaults.secondaryTextFieldColors,
|
||||
)
|
||||
|
||||
when (val networks = state.networks) {
|
||||
is NFTReceiveUM.Networks.Content -> NFTReceiveNetworksContent(networks)
|
||||
is NFTReceiveUM.Networks.Empty -> NFTReceiveNetworksEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
ShowBottomSheet(state.bottomSheetConfig)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
package com.tangem.features.nft.traits.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
|
|
@ -17,25 +16,21 @@ import com.tangem.features.nft.traits.entity.NFTAssetTraitsUM
|
|||
internal fun NFTAssetTraits(state: NFTAssetTraitsUM, modifier: Modifier = Modifier) {
|
||||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
topBar = {
|
||||
TangemTopAppBar(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
startButton = TopAppBarButtonUM(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onIconClicked = state.onBackClick,
|
||||
),
|
||||
title = stringResourceSafe(R.string.nft_traits_title),
|
||||
)
|
||||
},
|
||||
content = { innerPadding ->
|
||||
NFTAssetTraitsContent(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding),
|
||||
state = state,
|
||||
)
|
||||
},
|
||||
)
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.secondary),
|
||||
) {
|
||||
TangemTopAppBar(
|
||||
modifier = Modifier,
|
||||
startButton = TopAppBarButtonUM(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onIconClicked = state.onBackClick,
|
||||
),
|
||||
title = stringResourceSafe(R.string.nft_traits_title),
|
||||
)
|
||||
|
||||
NFTAssetTraitsContent(
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -48,10 +48,14 @@ internal sealed class CommonSendAnalyticEvents(
|
|||
data class TransactionError(
|
||||
val categoryName: String,
|
||||
val token: String,
|
||||
val blockchain: String,
|
||||
) : CommonSendAnalyticEvents(
|
||||
category = categoryName,
|
||||
event = "Error - Transaction Rejected",
|
||||
params = mapOf(TOKEN_PARAM to token),
|
||||
params = mapOf(
|
||||
TOKEN_PARAM to token,
|
||||
BLOCKCHAIN to blockchain,
|
||||
),
|
||||
)
|
||||
|
||||
/** Close button clicked */
|
||||
|
|
|
|||
|
|
@ -404,6 +404,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
CommonSendAnalyticEvents.TransactionError(
|
||||
categoryName = analyticsCategoryName,
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -312,6 +312,7 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
CommonSendAnalyticEvents.TransactionError(
|
||||
categoryName = analyticsCategoryName,
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
|
|||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.BlockchainErrorInfo
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.staking.*
|
||||
import com.tangem.domain.staking.analytics.StakeScreenSource
|
||||
|
|
@ -37,6 +36,7 @@ import com.tangem.domain.staking.model.StakingApproval
|
|||
import com.tangem.domain.staking.model.stakekit.*
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||
import com.tangem.domain.staking.utils.getValidatorsCount
|
||||
import com.tangem.domain.tokens.*
|
||||
|
|
@ -120,6 +120,7 @@ internal class StakingModel @Inject constructor(
|
|||
private val getActionsUseCase: GetActionsUseCase,
|
||||
private val getYieldUseCase: GetYieldUseCase,
|
||||
private val checkAccountInitializedUseCase: CheckAccountInitializedUseCase,
|
||||
private val getActionRequirementAmountUseCase: GetActionRequirementAmountUseCase,
|
||||
private val paramsInterceptorHolder: ParamsInterceptorHolder,
|
||||
private val shareManager: ShareManager,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
|
|
@ -527,9 +528,20 @@ internal class StakingModel @Inject constructor(
|
|||
val rewardPendingActionConstraints = yieldBalance?.reward?.rewardConstraints
|
||||
|
||||
if (rewardBlockType == RewardBlockType.RewardsRequirementsError) {
|
||||
val minimumAmount = rewardPendingActionConstraints?.amountArg?.minimum
|
||||
// Temporary fix, until StakeKit adds minimum requirement amount to balance response
|
||||
val minimumAmountValue = if (minimumAmount == null && yieldBalance.integrationId != null) {
|
||||
getActionRequirementAmountUseCase.invoke(
|
||||
integrationId = yieldBalance.integrationId,
|
||||
actionType = StakingActionType.CLAIM_REWARDS,
|
||||
).getOrNull()
|
||||
} else {
|
||||
minimumAmount
|
||||
}
|
||||
|
||||
stakingEventFactory.createStakingRewardsMinimumRequirementsErrorAlert(
|
||||
cryptoCurrencyName = cryptoCurrencyStatus.currency.name,
|
||||
cryptoAmountValue = rewardPendingActionConstraints?.amountArg?.minimum?.format {
|
||||
cryptoAmountValue = minimumAmountValue?.format {
|
||||
crypto(cryptoCurrencyStatus.currency)
|
||||
}.orEmpty(),
|
||||
)
|
||||
|
|
@ -931,11 +943,6 @@ internal class StakingModel @Inject constructor(
|
|||
isSingleWalletWithTokens = false,
|
||||
)
|
||||
.conflate()
|
||||
.filter {
|
||||
val sources = it.getOrNull()?.value?.sources ?: return@filter true
|
||||
|
||||
sources.networkSource == StatusSource.ACTUAL && sources.yieldBalanceSource == StatusSource.ACTUAL
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.filter { value.currentStep == StakingStep.InitialInfo }
|
||||
.onEach { maybeStatus ->
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import java.math.BigDecimal
|
|||
@Immutable
|
||||
internal sealed class InnerYieldBalanceState {
|
||||
data class Data(
|
||||
val integrationId: String?,
|
||||
val reward: YieldReward,
|
||||
val isActionable: Boolean,
|
||||
val balances: ImmutableList<BalanceState>,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ internal class YieldBalancesConverter(
|
|||
?.firstOrNull { it.type == StakingActionType.CLAIM_REWARDS }
|
||||
|
||||
InnerYieldBalanceState.Data(
|
||||
integrationId = yieldBalance?.integrationId,
|
||||
reward = YieldReward(
|
||||
rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) },
|
||||
rewardsFiat = fiatRewardsValue.format {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,11 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
coroutineScope {
|
||||
listOf(
|
||||
async {
|
||||
fetchCurrencyStatus()
|
||||
/*
|
||||
* It is important to use NonCancellable here to ensure the update is not interrupted midway.
|
||||
* For example, this can happen if the user enters and immediately leaves the screen.
|
||||
*/
|
||||
withContext(NonCancellable) { fetchCurrencyStatus() }
|
||||
},
|
||||
async {
|
||||
updateStakingActions()
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ internal object InitialStakingStatePreview {
|
|||
|
||||
val stateWithYield = defaultState.copy(
|
||||
yieldBalance = InnerYieldBalanceState.Data(
|
||||
integrationId = null,
|
||||
reward = YieldReward(
|
||||
rewardsFiat = "100 $",
|
||||
rewardsCrypto = "100 SOL",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.staking.impl.presentation.state.utils
|
|||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||
|
|
@ -44,16 +45,18 @@ internal fun isSingleAction(networkId: String, activeStake: BalanceState): Boole
|
|||
return isSingleAction && !isRestake || isCompositePendingActions
|
||||
}
|
||||
|
||||
internal fun withStubUnstakeAction(networkId: String, activeStake: BalanceState) = if (isStubUnstakeAction(networkId)) {
|
||||
activeStake.pendingActions.plus(
|
||||
PendingAction(
|
||||
type = StakingActionType.UNSTAKE,
|
||||
passthrough = "",
|
||||
args = null,
|
||||
),
|
||||
).toPersistentList()
|
||||
} else {
|
||||
activeStake.pendingActions
|
||||
internal fun withStubUnstakeAction(networkId: String, activeStake: BalanceState): ImmutableList<PendingAction> {
|
||||
return if (isStubUnstakeAction(networkId) && activeStake.type != BalanceType.REWARDS) {
|
||||
activeStake.pendingActions.plus(
|
||||
PendingAction(
|
||||
type = StakingActionType.UNSTAKE,
|
||||
passthrough = "",
|
||||
args = null,
|
||||
),
|
||||
).toPersistentList()
|
||||
} else {
|
||||
activeStake.pendingActions
|
||||
}
|
||||
}
|
||||
|
||||
internal fun isTronStakedBalance(networkId: String, pendingAction: PendingAction?): Boolean {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore
|
|||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectList
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectListSync
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectMapSync
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectMap
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
|
@ -17,9 +17,8 @@ import com.tangem.feature.swap.domain.models.domain.*
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class DefaultSwapTransactionRepository(
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
|
|
@ -82,35 +81,35 @@ internal class DefaultSwapTransactionRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getTransactions(
|
||||
override fun getTransactions(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
): Flow<List<SavedSwapTransactionListModel>?> {
|
||||
return withContext(dispatchers.io) {
|
||||
val txStatuses = appPreferencesStore.getObjectMapSync<ExchangeStatusModel>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
)
|
||||
appPreferencesStore.getObjectList<SavedSwapTransactionListModelInner>(
|
||||
return combine(
|
||||
flow = appPreferencesStore.getObjectList<SavedSwapTransactionListModelInner>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
|
||||
).map { savedTransactions ->
|
||||
val currencyTxs = savedTransactions
|
||||
?.filter {
|
||||
it.userWalletId == userWallet.walletId.stringValue &&
|
||||
(
|
||||
it.toCryptoCurrencyId == cryptoCurrencyId.value ||
|
||||
it.fromCryptoCurrencyId == cryptoCurrencyId.value
|
||||
)
|
||||
}
|
||||
),
|
||||
flow2 = appPreferencesStore.getObjectMap<ExchangeStatusModel>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
),
|
||||
) { savedTransactions, txStatuses ->
|
||||
val currencyTxs = savedTransactions?.filter {
|
||||
it.userWalletId == userWallet.walletId.stringValue &&
|
||||
(
|
||||
it.toCryptoCurrencyId == cryptoCurrencyId.value ||
|
||||
it.fromCryptoCurrencyId == cryptoCurrencyId.value
|
||||
)
|
||||
}
|
||||
|
||||
currencyTxs?.mapNotNull {
|
||||
converter.convertBack(
|
||||
value = it,
|
||||
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
|
||||
txStatuses = txStatuses,
|
||||
)
|
||||
}
|
||||
}.flowOn(dispatchers.io)
|
||||
currencyTxs?.mapNotNull {
|
||||
converter.convertBack(
|
||||
value = it,
|
||||
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
|
||||
txStatuses = txStatuses,
|
||||
)
|
||||
}
|
||||
}
|
||||
.flowOn(dispatchers.default)
|
||||
}
|
||||
|
||||
override suspend fun removeTransaction(
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ interface SwapTransactionRepository {
|
|||
transaction: SavedSwapTransactionModel,
|
||||
)
|
||||
|
||||
suspend fun getTransactions(
|
||||
fun getTransactions(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
): Flow<List<SavedSwapTransactionListModel>?>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.model
|
|||
import androidx.compose.runtime.Stable
|
||||
import androidx.paging.cachedIn
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.merge
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
|
|
@ -11,7 +12,9 @@ import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
|
|||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
|
|
@ -134,6 +137,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
private val appRouter: AppRouter,
|
||||
private val router: InnerTokenDetailsRouter,
|
||||
private val tokenDetailsDeepLinkActionListener: TokenDetailsDeepLinkActionListener,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
) : Model(), TokenDetailsClickIntents {
|
||||
|
||||
private val params = paramsContainer.require<TokenDetailsComponent.Params>()
|
||||
|
|
@ -205,15 +209,15 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
checkForActionUpdates()
|
||||
}
|
||||
|
||||
fun onResume() {
|
||||
subscribeOnExpressTransactionsUpdates()
|
||||
}
|
||||
|
||||
fun onPause() {
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
expressTxJobHolder.cancel()
|
||||
}
|
||||
|
||||
fun onResume() {
|
||||
subscribeOnExpressTransactionsUpdates()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
expressTxJobHolder.cancel()
|
||||
|
|
@ -304,66 +308,61 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun subscribeOnCurrencyStatusUpdates() {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = cryptoCurrency.id,
|
||||
isSingleWalletWithTokens = userWallet is UserWallet.Cold &&
|
||||
userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(),
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.onEach { maybeCurrencyStatus ->
|
||||
internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus)
|
||||
maybeCurrencyStatus.onRight { status ->
|
||||
cryptoCurrencyStatus = status
|
||||
updateButtons(currencyStatus = status)
|
||||
updateWarnings(status)
|
||||
subscribeOnUpdateStakingInfo(status)
|
||||
}
|
||||
currencyStatusAnalyticsSender.send(maybeCurrencyStatus)
|
||||
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = cryptoCurrency.id,
|
||||
isSingleWalletWithTokens = userWallet is UserWallet.Cold &&
|
||||
userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(),
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.onEach { maybeCurrencyStatus ->
|
||||
internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus)
|
||||
maybeCurrencyStatus.onRight { status ->
|
||||
cryptoCurrencyStatus = status
|
||||
updateButtons(currencyStatus = status)
|
||||
updateWarnings(status)
|
||||
subscribeOnUpdateStakingInfo(status)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(marketPriceJobHolder)
|
||||
}
|
||||
currencyStatusAnalyticsSender.send(maybeCurrencyStatus)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(marketPriceJobHolder)
|
||||
}
|
||||
|
||||
private fun subscribeOnExpressTransactionsUpdates() {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
expressStatusFactory
|
||||
.getExpressStatuses()
|
||||
.distinctUntilChanged()
|
||||
.onEach { waitForFirstExpressStatusEmmit.value = true }
|
||||
.onEach { expressTxs ->
|
||||
internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs(
|
||||
expressTxs,
|
||||
::updateNetworkToSwapBalance,
|
||||
)
|
||||
expressTxStatusTaskScheduler.scheduleTask(
|
||||
modelScope,
|
||||
PeriodicTask(
|
||||
isDelayFirst = false,
|
||||
delay = EXPRESS_STATUS_UPDATE_DELAY,
|
||||
task = {
|
||||
runCatching {
|
||||
expressStatusFactory.getUpdatedExpressStatuses(internalUiState.value.expressTxs)
|
||||
}
|
||||
},
|
||||
onSuccess = { updatedTxs ->
|
||||
internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs(
|
||||
updatedTxs,
|
||||
::updateNetworkToSwapBalance,
|
||||
)
|
||||
},
|
||||
onError = { /* no-op */ },
|
||||
),
|
||||
)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(expressTxJobHolder)
|
||||
}
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
expressStatusFactory.getExpressStatuses()
|
||||
.distinctUntilChanged()
|
||||
.onEach { waitForFirstExpressStatusEmmit.value = true }
|
||||
.onEach { expressTxs ->
|
||||
internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs(
|
||||
expressTxs = expressTxs,
|
||||
updateBalance = ::updateNetworkToSwapBalance,
|
||||
)
|
||||
expressTxStatusTaskScheduler.scheduleTask(
|
||||
scope = modelScope,
|
||||
task = PeriodicTask(
|
||||
isDelayFirst = false,
|
||||
delay = EXPRESS_STATUS_UPDATE_DELAY,
|
||||
task = {
|
||||
runCatching {
|
||||
expressStatusFactory.getUpdatedExpressStatuses(internalUiState.value.expressTxs)
|
||||
}
|
||||
},
|
||||
onSuccess = { updatedTxs ->
|
||||
internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs(
|
||||
updatedTxs,
|
||||
::updateNetworkToSwapBalance,
|
||||
)
|
||||
},
|
||||
onError = { /* no-op */ },
|
||||
),
|
||||
)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(expressTxJobHolder)
|
||||
}
|
||||
|
||||
private fun updateNetworkToSwapBalance(toCryptoCurrency: CryptoCurrency) {
|
||||
|
|
@ -449,7 +448,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
network = cryptoCurrency.network,
|
||||
).getOrElse { false }
|
||||
|
||||
val isSupported = getExtendedPublicKeyForCurrencyUseCase.isSupported(userWalletId, cryptoCurrency.network)
|
||||
val isSupported = isXPUBSupported()
|
||||
|
||||
internalUiState.value = stateFactory.getStateWithUpdatedMenu(
|
||||
cardTypesResolver = userWallet.scanResponse.cardTypesResolver,
|
||||
|
|
@ -459,6 +458,34 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun isXPUBSupported(): Boolean {
|
||||
return getExtendedPublicKeyForCurrencyUseCase.isSupported(
|
||||
userWalletId = userWalletId,
|
||||
network = cryptoCurrency.network,
|
||||
)
|
||||
.mapLeft {
|
||||
analyticsExceptionHandler.sendException(
|
||||
event = ExceptionAnalyticsEvent(
|
||||
exception = it,
|
||||
params = mapOf(
|
||||
"blockchainId" to cryptoCurrency.network.id.rawId.value,
|
||||
"networkId" to cryptoCurrency.network.backendId,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
Timber.e(
|
||||
/* t = */ it,
|
||||
/* message = */ "Unable to get wallet manager for user wallet %s and network %s",
|
||||
/* ...args = */ userWalletId,
|
||||
cryptoCurrency.network,
|
||||
)
|
||||
|
||||
false
|
||||
}
|
||||
.merge()
|
||||
}
|
||||
|
||||
private fun createSelectedAppCurrencyFlow(): StateFlow<AppCurrency> {
|
||||
return getSelectedAppCurrencyUseCase()
|
||||
.map { maybeAppCurrency ->
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(): Flow<PersistentList<ExchangeUM>> {
|
||||
operator fun invoke(): Flow<PersistentList<ExchangeUM>> {
|
||||
return swapTransactionRepository.getTransactions(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
|
|
|
|||
|
|
@ -65,14 +65,12 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun getExpressStatuses(): Flow<PersistentList<ExpressTransactionStateUM>> = combine(
|
||||
fun getExpressStatuses(): Flow<PersistentList<ExpressTransactionStateUM>> = combine(
|
||||
flow = exchangeStatusFactory(),
|
||||
flow2 = onrampStatusFactory(),
|
||||
) { maybeExchange, maybeOnramp ->
|
||||
persistentListOf(
|
||||
maybeOnramp,
|
||||
maybeExchange,
|
||||
).flatten()
|
||||
persistentListOf(maybeOnramp, maybeExchange)
|
||||
.flatten()
|
||||
.sortedByDescending { it.info.timestamp }
|
||||
.toPersistentList()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.domain.nft.DisableWalletNFTUseCase
|
||||
import com.tangem.domain.nft.EnableWalletNFTUseCase
|
||||
import com.tangem.domain.nft.GetWalletNFTEnabledUseCase
|
||||
import com.tangem.domain.notifications.GetApplicationIdUseCase
|
||||
import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles
|
||||
import com.tangem.domain.settings.repositories.PermissionRepository
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -45,6 +46,7 @@ import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnaly
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
|
@ -72,6 +74,8 @@ internal class WalletSettingsModel @Inject constructor(
|
|||
private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val permissionsRepository: PermissionRepository,
|
||||
private val getApplicationIdUseCase: GetApplicationIdUseCase,
|
||||
private val associateWalletsWithApplicationIdUseCase: AssociateWalletsWithApplicationIdUseCase,
|
||||
) : Model() {
|
||||
|
||||
val params: WalletSettingsComponent.Params = paramsContainer.require()
|
||||
|
|
@ -192,10 +196,20 @@ internal class WalletSettingsModel @Inject constructor(
|
|||
if (hasUserWallets) {
|
||||
router.pop()
|
||||
} else {
|
||||
clearWalletsAssociatedWithApplicationId()
|
||||
router.replaceAll(AppRoute.Home)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearWalletsAssociatedWithApplicationId() = modelScope.launch(NonCancellable) {
|
||||
getApplicationIdUseCase().onRight { applicationId ->
|
||||
associateWalletsWithApplicationIdUseCase(applicationId, emptyList())
|
||||
.onLeft {
|
||||
Timber.e("Unable to associate empty wallets with application ID: $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onLinkMoreCardsClick(scanResponse: ScanResponse) {
|
||||
analyticsEventHandler.send(Settings.ButtonCreateBackup)
|
||||
analyticsContextProxy.addContext(scanResponse)
|
||||
|
|
|
|||
|
|
@ -58,11 +58,9 @@ internal abstract class BasicTokenListSubscriber(
|
|||
}
|
||||
.distinctUntilChanged()
|
||||
.onEach { maybeTokenList ->
|
||||
if (!routingFeatureToggle.isDeepLinkNavigationEnabled) {
|
||||
coroutineScope.launch {
|
||||
onTokenListReceived(maybeTokenList)
|
||||
}.saveIn(onTokenListReceivedJobHolder)
|
||||
}
|
||||
coroutineScope.launch {
|
||||
onTokenListReceived(maybeTokenList)
|
||||
}.saveIn(onTokenListReceivedJobHolder)
|
||||
|
||||
coroutineScope.launch { startCheck(maybeTokenList) }
|
||||
},
|
||||
|
|
@ -77,8 +75,7 @@ internal abstract class BasicTokenListSubscriber(
|
|||
ifLoading = { maybeContent ->
|
||||
val isRefreshing = stateHolder.getWalletState(userWallet.walletId)
|
||||
?.pullToRefreshConfig
|
||||
?.isRefreshing
|
||||
?: false
|
||||
?.isRefreshing == true
|
||||
|
||||
maybeContent
|
||||
?.takeIf { !isRefreshing }
|
||||
|
|
@ -118,15 +115,17 @@ internal abstract class BasicTokenListSubscriber(
|
|||
}
|
||||
|
||||
protected open suspend fun onTokenListReceived(maybeTokenList: Lce<TokenListError, TokenList>) {
|
||||
/* no-op */
|
||||
// Handling sell deeplink requires full content in order to correctly open Send screen
|
||||
if (maybeTokenList.getOrNull(false) != null) {
|
||||
deepLinksRegistry.triggerDelayedDeeplink(deepLinkClass = SellCurrencyDeepLink::class.java)
|
||||
}
|
||||
// Handling referral deeplink requires only selected wallet to be loaded
|
||||
// This is temporary solution, will be removed with complete deeplink navigation overhaul
|
||||
if (maybeTokenList.getOrNull(true) != null) {
|
||||
deepLinksRegistry.triggerDelayedDeeplink(deepLinkClass = ReferralDeepLink::class.java)
|
||||
if (!routingFeatureToggle.isDeepLinkNavigationEnabled) {
|
||||
/* no-op */
|
||||
// Handling sell deeplink requires full content in order to correctly open Send screen
|
||||
if (maybeTokenList.getOrNull(false) != null) {
|
||||
deepLinksRegistry.triggerDelayedDeeplink(deepLinkClass = SellCurrencyDeepLink::class.java)
|
||||
}
|
||||
// Handling referral deeplink requires only selected wallet to be loaded
|
||||
// This is temporary solution, will be removed with complete deeplink navigation overhaul
|
||||
if (maybeTokenList.getOrNull(true) != null) {
|
||||
deepLinksRegistry.triggerDelayedDeeplink(deepLinkClass = ReferralDeepLink::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# https://github.com/tangem/tangem-sdk-android/
|
||||
# https://github.com/tangem/vico
|
||||
|
||||
tangemBlockchainSdk = "develop-1104"
|
||||
tangemBlockchainSdk = "develop-1110"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-489"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue