Updated on 2026-08-14
This commit is contained in:
commit
64a5db094e
30 changed files with 395 additions and 550 deletions
|
|
@ -5,6 +5,7 @@ import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
|
|||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
import com.tangem.utils.extensions.replaceBy
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
|
|
@ -27,4 +28,23 @@ internal class DefaultNetworksStatusesStore(
|
|||
store(key, newValues)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun storeAll(key: UserWalletId, values: Collection<NetworkStatus>) {
|
||||
mutex.withLock {
|
||||
val currentValues = getSyncOrNull(key) ?: emptySet()
|
||||
val updatedValues = currentValues.toMutableSet()
|
||||
|
||||
values.forEach { newValue ->
|
||||
val isReplaced = updatedValues.replaceBy(newValue) {
|
||||
it.network == newValue.network
|
||||
}
|
||||
|
||||
if (!isReplaced) {
|
||||
updatedValues.add(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
store(key, updatedValues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,4 +11,6 @@ interface NetworksStatusesStore {
|
|||
suspend fun getSyncOrNull(key: UserWalletId): Set<NetworkStatus>?
|
||||
|
||||
suspend fun store(key: UserWalletId, value: NetworkStatus)
|
||||
|
||||
suspend fun storeAll(key: UserWalletId, values: Collection<NetworkStatus>)
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package com.tangem.data.staking
|
|||
|
||||
import android.util.Base64
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.raise.catch
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
|
|
@ -30,8 +29,6 @@ import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrap
|
|||
import com.tangem.datasource.api.stakekit.models.response.model.transaction.tron.TronStakeKitTransaction
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.lce.lceFlow
|
||||
import com.tangem.domain.staking.model.StakingApproval
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
|
|
@ -57,6 +54,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
import com.tangem.utils.extensions.orZero
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -103,10 +101,6 @@ internal class DefaultStakingRepository(
|
|||
private val yieldBalanceConverter = YieldBalanceConverter()
|
||||
private val yieldBalanceListConverter = YieldBalanceListConverter(yieldBalanceConverter)
|
||||
|
||||
private val isYieldBalanceFetching = MutableStateFlow(
|
||||
value = emptyMap<UserWalletId, Boolean>(),
|
||||
)
|
||||
|
||||
private val tronStakeKitTransactionAdapter by lazy { moshi.adapter(TronStakeKitTransaction::class.java) }
|
||||
|
||||
override fun getIntegrationKey(cryptoCurrencyId: CryptoCurrency.ID): String = with(cryptoCurrencyId) {
|
||||
|
|
@ -385,88 +379,66 @@ internal class DefaultStakingRepository(
|
|||
refresh: Boolean,
|
||||
) = withContext(dispatchers.io) {
|
||||
if (!stakingFeatureToggle.isStakingEnabled) return@withContext
|
||||
try {
|
||||
isYieldBalanceFetching.update {
|
||||
it + (userWalletId to true)
|
||||
}
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getYieldBalancesKey(userWalletId),
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
val yields = getEnabledYields()
|
||||
val availableCurrencies = cryptoCurrencies
|
||||
.mapNotNull { currency ->
|
||||
val addresses = walletManagersFacade.getAddresses(userWalletId, currency.network)
|
||||
val integrationId = integrationIdMap[getIntegrationKey(currency.id)]
|
||||
|
||||
if (integrationId != null && yields.any { it.id == integrationId }) {
|
||||
addresses to integrationId
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.flatMap { (addresses, integrationId) ->
|
||||
addresses.map { address -> address to integrationId }
|
||||
}
|
||||
.map { getBalanceRequestData(it.first.value, it.second) }
|
||||
.ifEmpty {
|
||||
cacheRegistry.invalidate(getYieldBalancesKey(userWalletId))
|
||||
error("No addresses found")
|
||||
}
|
||||
val result = stakeKitApi.getMultipleYieldBalances(availableCurrencies).getOrThrow()
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getYieldBalancesKey(userWalletId),
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
val yields = getEnabledYields().ifEmpty {
|
||||
Timber.i("No enabled yields for $userWalletId")
|
||||
stakingBalanceStore.store(userWalletId, emptySet())
|
||||
|
||||
stakingBalanceStore.store(userWalletId, result)
|
||||
},
|
||||
)
|
||||
} finally {
|
||||
isYieldBalanceFetching.update {
|
||||
it - userWalletId
|
||||
}
|
||||
}
|
||||
return@invokeOnExpire
|
||||
}
|
||||
val availableCurrencies = cryptoCurrencies
|
||||
.mapNotNull { currency ->
|
||||
val addresses = walletManagersFacade.getAddresses(userWalletId, currency.network)
|
||||
val integrationId = integrationIdMap[getIntegrationKey(currency.id)]
|
||||
|
||||
if (integrationId != null && yields.any { it.id == integrationId }) {
|
||||
addresses to integrationId
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.flatMap { (addresses, integrationId) ->
|
||||
addresses.map { address -> address to integrationId }
|
||||
}
|
||||
.map { getBalanceRequestData(it.first.value, it.second) }
|
||||
.ifEmpty {
|
||||
Timber.i("No yield balances available for $userWalletId")
|
||||
stakingBalanceStore.store(userWalletId, emptySet())
|
||||
|
||||
cacheRegistry.invalidate(getYieldBalancesKey(userWalletId))
|
||||
|
||||
return@invokeOnExpire
|
||||
}
|
||||
|
||||
val result = stakeKitApi
|
||||
.getMultipleYieldBalances(availableCurrencies)
|
||||
.getOrThrow()
|
||||
|
||||
stakingBalanceStore.store(userWalletId, result)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun getMultiYieldBalanceFlow(
|
||||
override fun getMultiYieldBalanceUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
): Flow<YieldBalanceList> = channelFlow {
|
||||
if (!stakingFeatureToggle.isStakingEnabled) {
|
||||
send(YieldBalanceList.Empty)
|
||||
} else {
|
||||
launch(dispatchers.io) {
|
||||
stakingBalanceStore.get(userWalletId)
|
||||
.collectLatest { send(yieldBalanceListConverter.convert(it)) }
|
||||
}
|
||||
stakingBalanceStore.get(userWalletId)
|
||||
.onEach {
|
||||
val balances = yieldBalanceListConverter.convert(it)
|
||||
send(balances)
|
||||
}
|
||||
.launchIn(scope = this + dispatchers.io)
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
fetchMultiYieldBalance(
|
||||
userWalletId,
|
||||
cryptoCurrencies,
|
||||
)
|
||||
}
|
||||
}
|
||||
}.cancellable()
|
||||
|
||||
override fun getMultiYieldBalanceLce(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
): LceFlow<Throwable, YieldBalanceList> = lceFlow {
|
||||
if (!stakingFeatureToggle.isStakingEnabled) {
|
||||
send(YieldBalanceList.Empty)
|
||||
} else {
|
||||
launch(dispatchers.io) {
|
||||
combine(
|
||||
stakingBalanceStore.get(userWalletId),
|
||||
isYieldBalanceFetching.map { it.getOrElse(userWalletId) { false } },
|
||||
) { result, isFetching ->
|
||||
val balances = yieldBalanceListConverter.convert(result)
|
||||
send(balances, isStillLoading = isFetching)
|
||||
}.collect()
|
||||
}
|
||||
withContext(dispatchers.io) {
|
||||
catch(
|
||||
block = { fetchMultiYieldBalance(userWalletId, cryptoCurrencies, refresh = false) },
|
||||
catch = { raise(it) },
|
||||
)
|
||||
fetchMultiYieldBalance(userWalletId, cryptoCurrencies, refresh = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.data.tokens.repository
|
||||
|
||||
import arrow.core.raise.catch
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.compatibility.getL2CompatibilityTokenComparison
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
|
|
@ -27,8 +26,6 @@ import com.tangem.datasource.local.preferences.utils.storeObject
|
|||
import com.tangem.datasource.local.token.ExpressAssetsStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.lce.lceFlow
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -42,6 +39,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import com.tangem.blockchain.common.FeePaidCurrency as FeePaidSdkCurrency
|
||||
|
|
@ -66,10 +64,6 @@ internal class DefaultCurrenciesRepository(
|
|||
private val userTokensBackwardCompatibility = UserTokensBackwardCompatibility()
|
||||
private val customTokensMerger = CustomTokensMerger(tangemTechApi, dispatchers)
|
||||
|
||||
private val isMultiCurrencyWalletCurrenciesFetching = MutableStateFlow(
|
||||
value = emptyMap<UserWalletId, Boolean>(),
|
||||
)
|
||||
|
||||
override suspend fun saveTokens(
|
||||
userWalletId: UserWalletId,
|
||||
currencies: List<CryptoCurrency>,
|
||||
|
|
@ -206,18 +200,14 @@ internal class DefaultCurrenciesRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getWalletCurrenciesUpdates(userWalletId: UserWalletId): LceFlow<Throwable, List<CryptoCurrency>> {
|
||||
return lceFlow {
|
||||
val userWallet = catch({ getUserWallet(userWalletId) }) {
|
||||
raise(it)
|
||||
}
|
||||
override fun getWalletCurrenciesUpdates(userWalletId: UserWalletId): Flow<List<CryptoCurrency>> {
|
||||
return channelFlow {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
|
||||
if (userWallet.isMultiCurrency) {
|
||||
getMultiCurrencyWalletCurrenciesUpdatesLce(userWalletId).collect(::send)
|
||||
getMultiCurrencyWalletCurrenciesUpdates(userWalletId).collect(::send)
|
||||
} else {
|
||||
val currency = catch({ getSingleCurrencyWalletPrimaryCurrency(userWalletId) }) {
|
||||
raise(it)
|
||||
}
|
||||
val currency = getSingleCurrencyWalletPrimaryCurrency(userWalletId)
|
||||
send(listOf(currency))
|
||||
}
|
||||
}
|
||||
|
|
@ -260,40 +250,14 @@ internal class DefaultCurrenciesRepository(
|
|||
val userWallet = getUserWallet(userWalletId)
|
||||
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true)
|
||||
|
||||
launch(dispatchers.io) {
|
||||
getMultiCurrencyWalletCurrencies(userWallet)
|
||||
.collectLatest(::send)
|
||||
}
|
||||
getMultiCurrencyWalletCurrencies(userWallet)
|
||||
.onEach { send(it) }
|
||||
.launchIn(scope = this + dispatchers.io)
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
fetchTokensIfCacheExpired(userWallet, refresh = false)
|
||||
}
|
||||
}
|
||||
.cancellable()
|
||||
}
|
||||
|
||||
override fun getMultiCurrencyWalletCurrenciesUpdatesLce(
|
||||
userWalletId: UserWalletId,
|
||||
): LceFlow<Throwable, List<CryptoCurrency>> = lceFlow {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
catch({ ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true) }) {
|
||||
raise(it)
|
||||
}
|
||||
|
||||
launch(dispatchers.io) {
|
||||
combine(
|
||||
getMultiCurrencyWalletCurrencies(userWallet),
|
||||
isMultiCurrencyWalletCurrenciesFetching.map { it.getOrElse(userWallet.walletId) { false } },
|
||||
) { currencies, isFetching ->
|
||||
send(currencies, isStillLoading = isFetching)
|
||||
}.collect()
|
||||
}
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
catch({ fetchTokensIfCacheExpired(userWallet, refresh = false) }) {
|
||||
raise(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getMultiCurrencyWalletCurrenciesSync(
|
||||
|
|
@ -545,19 +509,7 @@ internal class DefaultCurrenciesRepository(
|
|||
cacheRegistry.invokeOnExpire(
|
||||
key = getTokensCacheKey(userWallet.walletId),
|
||||
skipCache = refresh,
|
||||
block = {
|
||||
isMultiCurrencyWalletCurrenciesFetching.update {
|
||||
it + (userWallet.walletId to true)
|
||||
}
|
||||
|
||||
try {
|
||||
fetchTokens(userWallet)
|
||||
} finally {
|
||||
isMultiCurrencyWalletCurrenciesFetching.update {
|
||||
it - userWallet.walletId
|
||||
}
|
||||
}
|
||||
},
|
||||
block = { fetchTokens(userWallet) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.data.tokens.repository
|
||||
|
||||
import arrow.core.raise.catch
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
|
|
@ -15,8 +14,6 @@ import com.tangem.datasource.local.preferences.PreferencesKeys
|
|||
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.lce.lceFlow
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
||||
|
|
@ -28,7 +25,10 @@ import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -46,42 +46,16 @@ internal class DefaultNetworksRepository(
|
|||
private val responseCurrenciesFactory by lazy { ResponseCryptoCurrenciesFactory() }
|
||||
private val networkStatusFactory by lazy { NetworkStatusFactory() }
|
||||
|
||||
private val isNetworkStatusesFetching = MutableStateFlow(
|
||||
value = emptyMap<UserWalletId, Boolean>(),
|
||||
)
|
||||
|
||||
override fun getNetworkStatusesUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
): Flow<Set<NetworkStatus>> = channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
networksStatusesStore.get(userWalletId)
|
||||
.collectLatest(::send)
|
||||
}
|
||||
networksStatusesStore.get(userWalletId)
|
||||
.onEach(::send)
|
||||
.launchIn(scope = this + dispatchers.io)
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
fetchNetworksStatusesIfCacheExpired(userWalletId, networks, false)
|
||||
}
|
||||
}
|
||||
.cancellable()
|
||||
|
||||
override fun getNetworkStatusesUpdatesLce(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
): LceFlow<Throwable, Set<NetworkStatus>> = lceFlow {
|
||||
launch(dispatchers.io) {
|
||||
combine(
|
||||
networksStatusesStore.get(userWalletId),
|
||||
isNetworkStatusesFetching.map { it.getOrElse(userWalletId) { false } },
|
||||
) { statuses, isFetching ->
|
||||
send(statuses, isStillLoading = isFetching)
|
||||
}.collect()
|
||||
}
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
catch({ fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh = false) }) {
|
||||
raise(it)
|
||||
}
|
||||
fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,83 +101,36 @@ internal class DefaultNetworksRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getNetworkAddress(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): CryptoCurrencyAddress = withContext(dispatchers.io) {
|
||||
CryptoCurrencyAddress(
|
||||
cryptoCurrency = currency,
|
||||
address = walletManagersFacade.getAddresses(userWalletId, currency.network)
|
||||
.firstOrNull { it.type == AddressType.Default }
|
||||
?.value.orEmpty(),
|
||||
)
|
||||
}
|
||||
|
||||
override fun getNetworkAddressFlow(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): Flow<CryptoCurrencyAddress> = channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
send(getNetworkAddress(userWalletId, currency))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getNetworkAddresses(userWalletId: UserWalletId): List<CryptoCurrencyAddress> =
|
||||
withContext(dispatchers.io) {
|
||||
// Get list of currencies matching [network]
|
||||
val currencies = getCurrencies(userWalletId)
|
||||
|
||||
// There is no currencies matching given [networks] in [userWalletId]
|
||||
if (currencies.toList().isEmpty()) return@withContext emptyList()
|
||||
|
||||
currencies.toList().map { currency ->
|
||||
CryptoCurrencyAddress(
|
||||
cryptoCurrency = currency,
|
||||
address = walletManagersFacade.getAddresses(userWalletId, currency.network)
|
||||
.firstOrNull { it.type == AddressType.Default }
|
||||
?.value.orEmpty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNetworkAddressesFlow(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): Flow<List<CryptoCurrencyAddress>> = channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
send(getNetworkAddresses(userWalletId, network))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNetworkAddressesFlow(userWalletId: UserWalletId): Flow<List<CryptoCurrencyAddress>> = channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
send(getNetworkAddresses(userWalletId))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchNetworksStatusesIfCacheExpired(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
refresh: Boolean,
|
||||
) {
|
||||
val currencies = getCurrencies(userWalletId, networks)
|
||||
val networksDeferred = networks.mapNotNull { network ->
|
||||
fetchNetworkStatusIfCacheExpired(userWalletId, network, currencies, refresh)
|
||||
) = coroutineScope {
|
||||
if (refresh) {
|
||||
val statusesToRefresh = networks.map { NetworkStatus(it, NetworkStatus.Refreshing) }
|
||||
networksStatusesStore.storeAll(userWalletId, statusesToRefresh)
|
||||
}
|
||||
|
||||
if (networksDeferred.isNotEmpty()) {
|
||||
try {
|
||||
isNetworkStatusesFetching.update {
|
||||
it + (userWalletId to true)
|
||||
}
|
||||
val currencies = getCurrencies(userWalletId, networks)
|
||||
val networksDeferred = networks.mapNotNull { network ->
|
||||
coroutineScope {
|
||||
val key = getNetworksStatusesCacheKey(userWalletId, network)
|
||||
|
||||
networksDeferred.awaitAll()
|
||||
} finally {
|
||||
isNetworkStatusesFetching.update {
|
||||
it - userWalletId
|
||||
if (refresh || cacheRegistry.isExpired(key)) {
|
||||
async {
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = key,
|
||||
skipCache = refresh,
|
||||
block = { fetchNetworkStatus(userWalletId, network, currencies) },
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
networksDeferred.awaitAll()
|
||||
}
|
||||
|
||||
private suspend fun fetchNetworksPendingTransactions(
|
||||
|
|
@ -222,26 +149,6 @@ internal class DefaultNetworksRepository(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchNetworkStatusIfCacheExpired(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
currencies: Sequence<CryptoCurrency>,
|
||||
refresh: Boolean,
|
||||
): Deferred<Unit>? = coroutineScope {
|
||||
val key = getNetworksStatusesCacheKey(userWalletId, network)
|
||||
if (refresh || cacheRegistry.isExpired(key)) {
|
||||
async {
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = key,
|
||||
skipCache = refresh,
|
||||
block = { fetchNetworkStatus(userWalletId, network, currencies) },
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchNetworkStatus(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.core.lce
|
||||
|
||||
import arrow.atomic.AtomicBoolean
|
||||
import arrow.core.raise.Raise
|
||||
import com.tangem.domain.core.utils.lceContent
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
|
|
@ -34,6 +35,8 @@ class LceFlowScope<E : Any, C : Any> @PublishedApi internal constructor(
|
|||
private val ifLoading: suspend LceFlowScope<E, C>.(C?) -> Unit,
|
||||
) : Raise<E>, CoroutineScope by producerScope {
|
||||
|
||||
val isLoading: AtomicBoolean = AtomicBoolean(value = true)
|
||||
|
||||
/**
|
||||
* Sends a error of type [E] within the [ProducerScope] and then closes it for send.
|
||||
* All subsequent sends will be ignored.
|
||||
|
|
@ -46,6 +49,8 @@ class LceFlowScope<E : Any, C : Any> @PublishedApi internal constructor(
|
|||
* @param r Error to raise.
|
||||
*/
|
||||
override fun raise(r: E): Nothing {
|
||||
isLoading.set(false)
|
||||
|
||||
producerScope.trySendBlocking(r.lceError())
|
||||
producerScope.close()
|
||||
|
||||
|
|
@ -66,6 +71,8 @@ class LceFlowScope<E : Any, C : Any> @PublishedApi internal constructor(
|
|||
* @param isStillLoading A flag indicating whether the content is still loading.
|
||||
*/
|
||||
suspend fun send(content: C, isStillLoading: Boolean = false) {
|
||||
isLoading.set(isStillLoading)
|
||||
|
||||
val value = if (isStillLoading) {
|
||||
ifLoading(content)
|
||||
return
|
||||
|
|
@ -89,6 +96,8 @@ class LceFlowScope<E : Any, C : Any> @PublishedApi internal constructor(
|
|||
suspend fun send(value: Lce<E, C>) {
|
||||
if (producerScope.isClosedForSend) return
|
||||
|
||||
isLoading.set(value.isLoading())
|
||||
|
||||
producerScope.send(value)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.domain.core.lce
|
||||
|
||||
import arrow.atomic.Atomic
|
||||
import arrow.core.Either
|
||||
import arrow.core.identity
|
||||
import arrow.core.raise.Raise
|
||||
import arrow.core.raise.RaiseDSL
|
||||
import arrow.core.raise.recover
|
||||
|
|
@ -97,6 +99,12 @@ class LceRaise<E : Any> @PublishedApi internal constructor(
|
|||
is Lce.Content -> content
|
||||
is Lce.Error -> raise(r = this)
|
||||
}
|
||||
|
||||
@RaiseDSL
|
||||
fun <C : Any> Either<E, C>.bindEither(): C = fold(
|
||||
ifLeft = { raise(it) },
|
||||
ifRight = ::identity,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.domain.staking.repositories
|
|||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.staking.model.StakingApproval
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
|
|
@ -50,16 +49,11 @@ interface StakingRepository {
|
|||
refresh: Boolean = false,
|
||||
)
|
||||
|
||||
fun getMultiYieldBalanceFlow(
|
||||
fun getMultiYieldBalanceUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
): Flow<YieldBalanceList>
|
||||
|
||||
fun getMultiYieldBalanceLce(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
): LceFlow<Throwable, YieldBalanceList>
|
||||
|
||||
suspend fun getMultiYieldBalanceSync(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,11 @@ data class NetworkStatus(
|
|||
*/
|
||||
sealed class Value
|
||||
|
||||
/**
|
||||
* Represents the state where the network is refreshing.
|
||||
*/
|
||||
data object Refreshing : Value()
|
||||
|
||||
/**
|
||||
* Represents the state where the network is unreachable.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -40,38 +40,39 @@ class FetchTokenListUseCase(
|
|||
* network statuses, and quotes for associated tokens.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param refresh Indicates whether to force a refresh of the token list data.
|
||||
* @param mode The refresh mode to control the fetching process.
|
||||
* @return An [Either] representing success (Right) or an error (Left) in fetching the token list.
|
||||
*/
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = false): Either<TokenListError, Unit> {
|
||||
return either {
|
||||
val currencies = fetchCurrencies(userWalletId, refresh)
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
mode: RefreshMode = RefreshMode.NONE,
|
||||
): Either<TokenListError, Unit> = either {
|
||||
val currencies = fetchCurrencies(userWalletId, refresh = mode.refreshCurrencies)
|
||||
|
||||
coroutineScope {
|
||||
val fetchStatuses = async {
|
||||
fetchNetworksStatuses(
|
||||
userWalletId,
|
||||
currencies.mapTo(hashSetOf()) { it.network },
|
||||
refresh,
|
||||
)
|
||||
}
|
||||
val fetchQuotes = async {
|
||||
fetchQuotes(
|
||||
currencies.mapTo(hashSetOf()) { it.id },
|
||||
refresh,
|
||||
)
|
||||
}
|
||||
|
||||
val yieldBalances = async {
|
||||
fetchYieldBalances(
|
||||
userWalletId = userWalletId,
|
||||
currencies = currencies,
|
||||
refresh = refresh,
|
||||
)
|
||||
}
|
||||
|
||||
awaitAll(fetchStatuses, fetchQuotes, yieldBalances)
|
||||
coroutineScope {
|
||||
val fetchStatuses = async {
|
||||
fetchNetworksStatuses(
|
||||
userWalletId,
|
||||
currencies.mapTo(hashSetOf()) { it.network },
|
||||
refresh = mode.refreshNetworksStatuses,
|
||||
)
|
||||
}
|
||||
val fetchQuotes = async {
|
||||
fetchQuotes(
|
||||
currencies.mapTo(hashSetOf()) { it.id },
|
||||
refresh = mode.refreshQuotes,
|
||||
)
|
||||
}
|
||||
|
||||
val yieldBalances = async {
|
||||
fetchYieldBalances(
|
||||
userWalletId = userWalletId,
|
||||
currencies = currencies,
|
||||
refresh = mode.refreshYieldBalances,
|
||||
)
|
||||
}
|
||||
|
||||
awaitAll(fetchStatuses, fetchQuotes, yieldBalances)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,4 +121,33 @@ class FetchTokenListUseCase(
|
|||
catch = { /* Ignore error */ },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the refresh modes available for fetching token list information.
|
||||
*/
|
||||
enum class RefreshMode(
|
||||
internal val refreshCurrencies: Boolean,
|
||||
internal val refreshNetworksStatuses: Boolean,
|
||||
internal val refreshQuotes: Boolean,
|
||||
internal val refreshYieldBalances: Boolean,
|
||||
) {
|
||||
NONE(
|
||||
refreshCurrencies = false,
|
||||
refreshNetworksStatuses = false,
|
||||
refreshQuotes = false,
|
||||
refreshYieldBalances = false,
|
||||
),
|
||||
FULL(
|
||||
refreshCurrencies = true,
|
||||
refreshNetworksStatuses = true,
|
||||
refreshQuotes = true,
|
||||
refreshYieldBalances = true,
|
||||
),
|
||||
SKIP_CURRENCIES(
|
||||
refreshCurrencies = false,
|
||||
refreshNetworksStatuses = true,
|
||||
refreshQuotes = true,
|
||||
refreshYieldBalances = true,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
@ -85,9 +85,6 @@ class GetWalletTotalBalanceUseCase(
|
|||
stakingRepository = stakingRepository,
|
||||
)
|
||||
|
||||
return operations.getCurrenciesStatuses(
|
||||
userWalletId = userWalletId,
|
||||
isSingleCurrencyWalletsAllowed = true,
|
||||
)
|
||||
return operations.getCurrenciesStatuses(userWalletId)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
package com.tangem.domain.tokens.operations
|
||||
|
||||
import arrow.core.*
|
||||
import arrow.core.raise.ensureNotNull
|
||||
import arrow.core.raise.recover
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.lce.lce
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
import com.tangem.domain.core.lce.lceFlow
|
||||
import com.tangem.domain.core.utils.EitherFlow
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
|
|
@ -26,147 +27,126 @@ internal class CurrenciesStatusesLceOperations(
|
|||
private val stakingRepository: StakingRepository,
|
||||
) {
|
||||
|
||||
fun getCurrenciesStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
isSingleCurrencyWalletsAllowed: Boolean = false,
|
||||
): LceFlow<TokenListError, List<CryptoCurrencyStatus>> {
|
||||
fun getCurrenciesStatuses(userWalletId: UserWalletId): LceFlow<TokenListError, List<CryptoCurrencyStatus>> {
|
||||
return transformToCurrenciesStatuses(
|
||||
userWalletId = userWalletId,
|
||||
flow = if (isSingleCurrencyWalletsAllowed) {
|
||||
getWalletCurrencies(userWalletId)
|
||||
} else {
|
||||
getMultiCurrencyWalletCurrencies(userWalletId)
|
||||
},
|
||||
currenciesFlow = getWalletCurrencies(userWalletId),
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun transformToCurrenciesStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
flow: LceFlow<TokenListError, List<CryptoCurrency>>,
|
||||
): LceFlow<TokenListError, List<CryptoCurrencyStatus>> {
|
||||
return flow.transformLatest transform@{ maybeCurrencies ->
|
||||
val nonEmptyCurrencies = maybeCurrencies.fold(
|
||||
ifLoading = { maybeContent ->
|
||||
emit(createLoadingCurrenciesStatuses(maybeContent))
|
||||
return@transform
|
||||
},
|
||||
ifContent = { content ->
|
||||
val nonEmptyCurrencies = content.toNonEmptyListOrNull()
|
||||
currenciesFlow: EitherFlow<TokenListError, List<CryptoCurrency>>,
|
||||
): LceFlow<TokenListError, List<CryptoCurrencyStatus>> = lceFlow {
|
||||
currenciesFlow.collectLatest { maybeCurrencies ->
|
||||
val nonEmptyCurrencies = maybeCurrencies.bind().toNonEmptyListOrNull()
|
||||
ensureNotNull(nonEmptyCurrencies) { TokenListError.EmptyTokens }
|
||||
|
||||
if (nonEmptyCurrencies == null) {
|
||||
emit(TokenListError.EmptyTokens.lceError())
|
||||
return@transform
|
||||
} else {
|
||||
nonEmptyCurrencies
|
||||
}
|
||||
},
|
||||
ifError = { error ->
|
||||
emit(error.lceError())
|
||||
return@transform
|
||||
},
|
||||
)
|
||||
// This is only 'true' when the flow here is empty, such as during initial loading
|
||||
if (isLoading.get()) {
|
||||
val loadingCurrencies = createCurrenciesStatuses(
|
||||
currencies = nonEmptyCurrencies,
|
||||
maybeNetworkStatuses = null,
|
||||
maybeQuotes = null,
|
||||
maybeYieldBalances = null,
|
||||
|
||||
)
|
||||
send(loadingCurrencies)
|
||||
}
|
||||
|
||||
val (networks, currenciesIds) = getIds(nonEmptyCurrencies)
|
||||
|
||||
fun createCurrenciesStatuses(
|
||||
maybeQuotes: Either<TokenListError, Set<Quote>>?,
|
||||
maybeNetworkStatuses: Either<TokenListError, Set<NetworkStatus>>?,
|
||||
maybeYieldBalances: Either<TokenListError, YieldBalanceList>?,
|
||||
): Lce<TokenListError, List<CryptoCurrencyStatus>> = createCurrenciesStatuses(
|
||||
currencies = nonEmptyCurrencies,
|
||||
maybeQuotes = maybeQuotes,
|
||||
maybeNetworkStatuses = maybeNetworkStatuses,
|
||||
maybeYieldBalances = maybeYieldBalances,
|
||||
)
|
||||
|
||||
combine(
|
||||
getQuotes(currenciesIds),
|
||||
getNetworksStatuses(userWalletId, networks),
|
||||
getYieldBalances(userWalletId, nonEmptyCurrencies),
|
||||
|
||||
) { maybeQuotes, maybeNetworksStatuses, maybeYieldBalances ->
|
||||
val statuses = createCurrenciesStatuses(
|
||||
currencies = nonEmptyCurrencies,
|
||||
maybeQuotes = maybeQuotes,
|
||||
maybeNetworkStatuses = maybeNetworksStatuses,
|
||||
maybeYieldBalances = maybeYieldBalances,
|
||||
)
|
||||
emit(statuses)
|
||||
}.collect()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLoadingCurrenciesStatuses(
|
||||
maybeCurrencies: List<CryptoCurrency>?,
|
||||
): Lce<TokenListError, List<CryptoCurrencyStatus>> {
|
||||
val nonEmptyCurrencies = maybeCurrencies?.toNonEmptyListOrNull()
|
||||
|
||||
val statuses = if (nonEmptyCurrencies == null) {
|
||||
lceLoading()
|
||||
} else {
|
||||
createCurrenciesStatuses(
|
||||
currencies = nonEmptyCurrencies,
|
||||
maybeNetworkStatuses = null,
|
||||
maybeQuotes = null,
|
||||
maybeYieldBalances = null,
|
||||
::createCurrenciesStatuses,
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.mapLatest { maybeCurrenciesStatuses ->
|
||||
send(maybeCurrenciesStatuses)
|
||||
}
|
||||
.launchIn(scope = this)
|
||||
}
|
||||
|
||||
return statuses
|
||||
}
|
||||
|
||||
private fun getWalletCurrencies(userWalletId: UserWalletId): LceFlow<TokenListError, List<CryptoCurrency>> {
|
||||
private fun getWalletCurrencies(userWalletId: UserWalletId): EitherFlow<TokenListError, List<CryptoCurrency>> {
|
||||
return currenciesRepository.getWalletCurrenciesUpdates(userWalletId)
|
||||
.map { maybeCurrencies ->
|
||||
maybeCurrencies.mapError { TokenListError.DataError(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMultiCurrencyWalletCurrencies(
|
||||
userWalletId: UserWalletId,
|
||||
): LceFlow<TokenListError, List<CryptoCurrency>> {
|
||||
return currenciesRepository.getMultiCurrencyWalletCurrenciesUpdatesLce(userWalletId)
|
||||
.map<List<CryptoCurrency>, Either<TokenListError, List<CryptoCurrency>>> { it.right() }
|
||||
.catch { emit(TokenListError.DataError(it).left()) }
|
||||
.distinctUntilChanged()
|
||||
.map { maybeCurrencies ->
|
||||
maybeCurrencies.mapError { TokenListError.DataError(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCurrenciesStatuses(
|
||||
currencies: NonEmptyList<CryptoCurrency>,
|
||||
maybeQuotes: Either<TokenListError, Set<Quote>>?,
|
||||
maybeNetworkStatuses: Lce<TokenListError, Set<NetworkStatus>>?,
|
||||
maybeYieldBalances: Lce<TokenListError, YieldBalanceList>?,
|
||||
maybeNetworkStatuses: Either<TokenListError, Set<NetworkStatus>>?,
|
||||
maybeYieldBalances: Either<TokenListError, YieldBalanceList>?,
|
||||
): Lce<TokenListError, List<CryptoCurrencyStatus>> = lce {
|
||||
isLoading.set(maybeNetworkStatuses == null)
|
||||
isLoading.set(maybeNetworkStatuses == null || maybeYieldBalances == null)
|
||||
|
||||
var quotesRetrievingFailed = false
|
||||
|
||||
val networksStatuses = maybeNetworkStatuses?.bindOrNull()?.toNonEmptySetOrNull()
|
||||
val networksStatuses = maybeNetworkStatuses?.bindEither()?.toNonEmptySetOrNull()
|
||||
val yieldBalances = maybeYieldBalances?.bindEither()
|
||||
val quotes = recover({ maybeQuotes?.bind()?.toNonEmptySetOrNull() }) {
|
||||
quotesRetrievingFailed = true
|
||||
null
|
||||
}?.ifEmpty {
|
||||
quotesRetrievingFailed = true
|
||||
null
|
||||
}
|
||||
|
||||
val yieldBalances = maybeYieldBalances?.getOrNull()
|
||||
if (quotes == null) {
|
||||
quotesRetrievingFailed = true
|
||||
}
|
||||
|
||||
currencies.map { currency ->
|
||||
val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
|
||||
val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network }
|
||||
val address = extractAddress(networkStatus)
|
||||
val supportedIntegration = stakingRepository.getSupportedIntegrationId(currency.id)
|
||||
val yieldBalance = if (supportedIntegration.isNullOrBlank().not()) {
|
||||
(yieldBalances as? YieldBalanceList.Data)?.getBalance(
|
||||
address = address,
|
||||
integrationId = supportedIntegration,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val yieldBalance = findYieldBalanceOrNull(yieldBalances, currency, networkStatus)
|
||||
|
||||
createCurrencyStatus(
|
||||
val currencyStatus = createCurrencyStatus(
|
||||
currency = currency,
|
||||
quote = quote,
|
||||
networkStatus = networkStatus,
|
||||
yieldBalance = yieldBalance,
|
||||
ignoreQuote = quotesRetrievingFailed,
|
||||
)
|
||||
|
||||
if (currencyStatus.value is CryptoCurrencyStatus.Loading) {
|
||||
isLoading.set(true)
|
||||
}
|
||||
|
||||
currencyStatus
|
||||
}
|
||||
}
|
||||
|
||||
private fun findYieldBalanceOrNull(
|
||||
yieldBalances: YieldBalanceList?,
|
||||
currency: CryptoCurrency,
|
||||
networkStatus: NetworkStatus?,
|
||||
): YieldBalance? {
|
||||
if (yieldBalances !is YieldBalanceList.Data) return null
|
||||
|
||||
val supportedIntegration = stakingRepository.getSupportedIntegrationId(currency.id)
|
||||
|
||||
if (supportedIntegration.isNullOrBlank()) return null
|
||||
|
||||
return yieldBalances.getBalance(
|
||||
address = extractAddress(networkStatus),
|
||||
integrationId = supportedIntegration,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createCurrencyStatus(
|
||||
currency: CryptoCurrency,
|
||||
quote: Quote?,
|
||||
|
|
@ -189,28 +169,27 @@ internal class CurrenciesStatusesLceOperations(
|
|||
return quotesRepository.getQuotesUpdates(tokensIds)
|
||||
.map<Set<Quote>, Either<TokenListError, Set<Quote>>> { it.right() }
|
||||
.catch { emit(TokenListError.DataError(it).left()) }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
private fun getNetworksStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
networks: NonEmptySet<Network>,
|
||||
): LceFlow<TokenListError, Set<NetworkStatus>> {
|
||||
return networksRepository.getNetworkStatusesUpdatesLce(userWalletId, networks)
|
||||
.map { maybeStatuses ->
|
||||
maybeStatuses.mapError { TokenListError.DataError(it) }
|
||||
}
|
||||
): EitherFlow<TokenListError, Set<NetworkStatus>> {
|
||||
return networksRepository.getNetworkStatusesUpdates(userWalletId, networks)
|
||||
.map<Set<NetworkStatus>, Either<TokenListError, Set<NetworkStatus>>> { it.right() }
|
||||
.catch { emit(TokenListError.DataError(it).left()) }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
private fun getYieldBalances(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
): LceFlow<TokenListError, YieldBalanceList> {
|
||||
return stakingRepository.getMultiYieldBalanceLce(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = cryptoCurrencies,
|
||||
).map { maybeBalances ->
|
||||
maybeBalances.mapError { TokenListError.DataError(it) }
|
||||
}
|
||||
): EitherFlow<TokenListError, YieldBalanceList> {
|
||||
return stakingRepository.getMultiYieldBalanceUpdates(userWalletId, cryptoCurrencies)
|
||||
.map<YieldBalanceList, Either<TokenListError, YieldBalanceList>> { it.right() }
|
||||
.catch { emit(TokenListError.DataError(it).left()) }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
private fun getIds(currencies: List<CryptoCurrency>): Pair<NonEmptySet<Network>, NonEmptySet<CryptoCurrency.ID>> {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ internal class CurrencyStatusOperations(
|
|||
|
||||
private fun createStatus(): CryptoCurrencyStatus.Value {
|
||||
return when (val status = networkStatus?.value) {
|
||||
null -> CryptoCurrencyStatus.Loading
|
||||
null,
|
||||
is NetworkStatus.Refreshing,
|
||||
-> CryptoCurrencyStatus.Loading
|
||||
is NetworkStatus.MissedDerivation -> createMissedDerivationStatus()
|
||||
is NetworkStatus.Unreachable -> createUnreachableStatus(status)
|
||||
is NetworkStatus.NoAccount -> createNoAccountStatus(status)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.domain.tokens.repository
|
||||
|
||||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.FeePaidCurrency
|
||||
|
|
@ -81,7 +80,7 @@ interface CurrenciesRepository {
|
|||
* @param userWalletId The unique identifier of the user wallet.
|
||||
* @return A list of [CryptoCurrency].
|
||||
*/
|
||||
fun getWalletCurrenciesUpdates(userWalletId: UserWalletId): LceFlow<Throwable, List<CryptoCurrency>>
|
||||
fun getWalletCurrenciesUpdates(userWalletId: UserWalletId): Flow<List<CryptoCurrency>>
|
||||
|
||||
/**
|
||||
* Retrieves the primary cryptocurrency for a specific single-currency user wallet.
|
||||
|
|
@ -130,17 +129,6 @@ interface CurrenciesRepository {
|
|||
*/
|
||||
fun getMultiCurrencyWalletCurrenciesUpdates(userWalletId: UserWalletId): Flow<List<CryptoCurrency>>
|
||||
|
||||
/**
|
||||
* Retrieves updates of the list of cryptocurrencies within a multi-currency wallet.
|
||||
*
|
||||
* Loads remote cryptocurrencies if they have expired.
|
||||
*
|
||||
* @param userWalletId The unique identifier of the user wallet.
|
||||
* @return A [LceFlow] emitting the set of cryptocurrencies associated with the user wallet. May emit an
|
||||
* [DataError.UserWalletError.WrongUserWallet] if single-currency user wallet ID provided.
|
||||
*/
|
||||
fun getMultiCurrencyWalletCurrenciesUpdatesLce(userWalletId: UserWalletId): LceFlow<Throwable, List<CryptoCurrency>>
|
||||
|
||||
/**
|
||||
* Retrieves the list of cryptocurrencies within a multi-currency wallet.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package com.tangem.domain.tokens.repository
|
||||
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
|
|
@ -23,20 +21,6 @@ interface NetworksRepository {
|
|||
*/
|
||||
fun getNetworkStatusesUpdates(userWalletId: UserWalletId, networks: Set<Network>): Flow<Set<NetworkStatus>>
|
||||
|
||||
/**
|
||||
* Retrieves updates of network statuses of specified blockchain networks for a specific user wallet.
|
||||
*
|
||||
* Loads remote network statuses if they have expired.
|
||||
*
|
||||
* @param userWalletId The unique identifier of the user wallet.
|
||||
* @param networks A set of network which statuses are to be retrieved.
|
||||
* @return A [LceFlow] emitting a set of [NetworkStatus] objects corresponding to the specified networks.
|
||||
*/
|
||||
fun getNetworkStatusesUpdatesLce(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
): LceFlow<Throwable, Set<NetworkStatus>>
|
||||
|
||||
/**
|
||||
* Fetches pending transactions for given network
|
||||
*
|
||||
|
|
@ -63,33 +47,8 @@ interface NetworksRepository {
|
|||
|
||||
fun isNeedToCreateAccountWithoutReserve(network: Network): Boolean
|
||||
|
||||
/**
|
||||
* Returns list of addresses and crypto currency info of added currencies of [network] in selected wallet [userWalletId]
|
||||
*/
|
||||
fun getNetworkAddressesFlow(userWalletId: UserWalletId, network: Network): Flow<List<CryptoCurrencyAddress>>
|
||||
|
||||
/**
|
||||
* Returns list of addresses and crypto currency info of added currencies of [network] in selected wallet [userWalletId]
|
||||
*/
|
||||
suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List<CryptoCurrencyAddress>
|
||||
|
||||
/**
|
||||
* Returns address of [cryptoCurrency] in selected wallet [userWalletId]
|
||||
*/
|
||||
suspend fun getNetworkAddress(userWalletId: UserWalletId, currency: CryptoCurrency): CryptoCurrencyAddress
|
||||
|
||||
/**
|
||||
* Returns address of [cryptoCurrency] in selected wallet [userWalletId]
|
||||
*/
|
||||
fun getNetworkAddressFlow(userWalletId: UserWalletId, currency: CryptoCurrency): Flow<CryptoCurrencyAddress>
|
||||
|
||||
/**
|
||||
* Returns list of addresses and crypto currency info in selected wallet [userWalletId]
|
||||
*/
|
||||
fun getNetworkAddressesFlow(userWalletId: UserWalletId): Flow<List<CryptoCurrencyAddress>>
|
||||
|
||||
/**
|
||||
* Returns list of addresses and crypto currency info in selected wallet [userWalletId]
|
||||
*/
|
||||
suspend fun getNetworkAddresses(userWalletId: UserWalletId): List<CryptoCurrencyAddress>
|
||||
}
|
||||
|
|
@ -3,8 +3,6 @@ package com.tangem.domain.tokens.repository
|
|||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.utils.toLce
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.FeePaidCurrency
|
||||
|
|
@ -57,7 +55,7 @@ internal class MockCurrenciesRepository(
|
|||
|
||||
override suspend fun removeCurrencies(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) = Unit
|
||||
|
||||
override fun getWalletCurrenciesUpdates(userWalletId: UserWalletId): LceFlow<Throwable, List<CryptoCurrency>> {
|
||||
override fun getWalletCurrenciesUpdates(userWalletId: UserWalletId): Flow<List<CryptoCurrency>> {
|
||||
return emptyFlow()
|
||||
}
|
||||
|
||||
|
|
@ -91,12 +89,6 @@ internal class MockCurrenciesRepository(
|
|||
return tokens.map { it.getOrElse { e -> throw e } }
|
||||
}
|
||||
|
||||
override fun getMultiCurrencyWalletCurrenciesUpdatesLce(
|
||||
userWalletId: UserWalletId,
|
||||
): LceFlow<Throwable, List<CryptoCurrency>> {
|
||||
return tokens.map { it.toLce() }
|
||||
}
|
||||
|
||||
override suspend fun getMultiCurrencyWalletCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
id: CryptoCurrency.ID,
|
||||
|
|
|
|||
|
|
@ -3,15 +3,11 @@ package com.tangem.domain.tokens.repository
|
|||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.utils.toLce
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
|
|
@ -26,13 +22,6 @@ internal class MockNetworksRepository(
|
|||
return statuses.map { it.getOrElse { e -> throw e } }
|
||||
}
|
||||
|
||||
override fun getNetworkStatusesUpdatesLce(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
): LceFlow<Throwable, Set<NetworkStatus>> {
|
||||
return statuses.map { it.toLce() }
|
||||
}
|
||||
|
||||
override suspend fun fetchNetworkPendingTransactions(userWalletId: UserWalletId, networks: Set<Network>) {
|
||||
// no-op
|
||||
}
|
||||
|
|
@ -47,37 +36,10 @@ internal class MockNetworksRepository(
|
|||
|
||||
override fun isNeedToCreateAccountWithoutReserve(network: Network) = false
|
||||
|
||||
override fun getNetworkAddressesFlow(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): Flow<List<CryptoCurrencyAddress>> = channelFlow {
|
||||
send(emptyList())
|
||||
}
|
||||
|
||||
override fun getNetworkAddressesFlow(userWalletId: UserWalletId): Flow<List<CryptoCurrencyAddress>> = channelFlow {
|
||||
send(emptyList())
|
||||
}
|
||||
|
||||
override suspend fun getNetworkAddresses(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): List<CryptoCurrencyAddress> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override suspend fun getNetworkAddresses(userWalletId: UserWalletId): List<CryptoCurrencyAddress> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override suspend fun getNetworkAddress(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): CryptoCurrencyAddress = CryptoCurrencyAddress(currency, "")
|
||||
|
||||
override fun getNetworkAddressFlow(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
): Flow<CryptoCurrencyAddress> = channelFlow {
|
||||
send(CryptoCurrencyAddress(currency, ""))
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,6 @@ import com.tangem.blockchain.common.Amount
|
|||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionStatus
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.lce.lceFlow
|
||||
import com.tangem.domain.staking.model.StakingApproval
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
|
|
@ -20,6 +18,7 @@ import com.tangem.domain.tokens.model.Network
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -146,27 +145,10 @@ class MockStakingRepository : StakingRepository {
|
|||
/* no-op */
|
||||
}
|
||||
|
||||
override fun getMultiYieldBalanceFlow(
|
||||
override fun getMultiYieldBalanceUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
): Flow<YieldBalanceList> = channelFlow {
|
||||
send(
|
||||
YieldBalanceList.Data(
|
||||
balances = listOf(YieldBalance.Error),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun getMultiYieldBalanceLce(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
): LceFlow<Throwable, YieldBalanceList> = lceFlow {
|
||||
send(
|
||||
YieldBalanceList.Data(
|
||||
balances = listOf(YieldBalance.Error),
|
||||
),
|
||||
)
|
||||
}
|
||||
): Flow<YieldBalanceList> = flowOf()
|
||||
|
||||
override suspend fun getMultiYieldBalanceSync(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import com.tangem.domain.demo.IsDemoCardUseCase
|
|||
import com.tangem.domain.promo.PromoBanner
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.settings.ShouldShowRingPromoUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -30,7 +29,7 @@ import kotlin.collections.count
|
|||
@Suppress("LongParameterList")
|
||||
@ViewModelScoped
|
||||
internal class GetMultiWalletWarningsFactory @Inject constructor(
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
private val shouldShowRingPromoUseCase: ShouldShowRingPromoUseCase,
|
||||
|
|
@ -44,7 +43,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
|
||||
val promoFlow = flow { emit(promoRepository.getRingPromoBanner()) }
|
||||
return combine(
|
||||
flow = getTokenListUseCase.launch(userWallet.walletId),
|
||||
flow = tokenListStore.getOrThrow(userWallet.walletId),
|
||||
flow2 = isReadyToShowRateAppUseCase(),
|
||||
flow3 = isNeedToBackupUseCase(userWallet.walletId),
|
||||
flow4 = shouldShowRingPromoUseCase(userWalletId = userWallet.walletId),
|
||||
|
|
@ -129,6 +128,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
val currencies = maybeTokenList.getMissingAddressCurrencies()
|
||||
.ifEmpty { return }
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Informational.MissingAddresses(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
@ViewModelScoped
|
||||
internal class MultiWalletTokenListStore @Inject constructor(
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
) {
|
||||
|
||||
private val flows: ConcurrentHashMap<UserWalletId, LceFlow<TokenListError, TokenList>> by lazy {
|
||||
ConcurrentHashMap()
|
||||
}
|
||||
|
||||
fun addIfNot(userWalletId: UserWalletId, coroutineScope: CoroutineScope) {
|
||||
if (flows[userWalletId] != null) {
|
||||
Timber.d("Flow with token list for $userWalletId already exists")
|
||||
return
|
||||
}
|
||||
|
||||
coroutineScope.ensureActive()
|
||||
|
||||
flows[userWalletId] = getTokenListUseCase
|
||||
.launch(userWalletId)
|
||||
.shareIn(
|
||||
scope = coroutineScope,
|
||||
started = SharingStarted.WhileSubscribed(),
|
||||
replay = 1,
|
||||
)
|
||||
|
||||
Timber.d("Flow with token list for $userWalletId created")
|
||||
}
|
||||
|
||||
fun getOrThrow(userWalletId: UserWalletId): LceFlow<TokenListError, TokenList> {
|
||||
return requireNotNull(flows[userWalletId]) {
|
||||
"Flow with token list for $userWalletId doesn't exist"
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(userWalletId: UserWalletId) {
|
||||
flows.remove(userWalletId)
|
||||
|
||||
Timber.d("Flow with token list for $userWalletId removed")
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
flows.clear()
|
||||
|
||||
Timber.d("All flows with token list cleared")
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,12 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
|||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletTokenListSubscriber
|
||||
|
|
@ -23,7 +23,7 @@ internal class MultiWalletContentLoader(
|
|||
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val walletWithFundsChecker: WalletWithFundsChecker,
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
|
|
@ -38,7 +38,7 @@ internal class MultiWalletContentLoader(
|
|||
clickIntents = clickIntents,
|
||||
tokenListAnalyticsSender = tokenListAnalyticsSender,
|
||||
walletWithFundsChecker = walletWithFundsChecker,
|
||||
getTokenListUseCase = getTokenListUseCase,
|
||||
tokenListStore = tokenListStore,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
|||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
|
|
@ -21,7 +21,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
private val walletWithFundsChecker: WalletWithFundsChecker,
|
||||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
|
|
@ -35,7 +35,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
stateHolder = stateHolder,
|
||||
tokenListAnalyticsSender = tokenListAnalyticsSender,
|
||||
walletWithFundsChecker = walletWithFundsChecker,
|
||||
getTokenListUseCase = getTokenListUseCase,
|
||||
tokenListStore = tokenListStore,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
|
|
|
|||
|
|
@ -41,11 +41,11 @@ internal abstract class BasicTokenListSubscriber(
|
|||
private val sendAnalyticsJobHolder = JobHolder()
|
||||
private val onTokenListReceivedJobHolder = JobHolder()
|
||||
|
||||
protected abstract fun tokenListFlow(): LceFlow<TokenListError, TokenList>
|
||||
protected abstract fun tokenListFlow(coroutineScope: CoroutineScope): LceFlow<TokenListError, TokenList>
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return combine(
|
||||
flow = tokenListFlow()
|
||||
flow = tokenListFlow(coroutineScope)
|
||||
.onEach { maybeTokenList ->
|
||||
coroutineScope.launch {
|
||||
sendTokenListAnalytics(maybeTokenList)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -12,14 +11,16 @@ import com.tangem.domain.tokens.model.TokenList
|
|||
import com.tangem.domain.tokens.model.TotalFiatBalance
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class MultiWalletTokenListSubscriber(
|
||||
private val userWallet: UserWallet,
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
stateHolder: WalletStateController,
|
||||
clickIntents: WalletClickIntents,
|
||||
|
|
@ -37,8 +38,10 @@ internal class MultiWalletTokenListSubscriber(
|
|||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
) {
|
||||
|
||||
override fun tokenListFlow(): LceFlow<TokenListError, TokenList> {
|
||||
return getTokenListUseCase.launch(userWallet.walletId)
|
||||
override fun tokenListFlow(coroutineScope: CoroutineScope): LceFlow<TokenListError, TokenList> {
|
||||
tokenListStore.addIfNot(userWallet.walletId, coroutineScope)
|
||||
|
||||
return tokenListStore.getOrThrow(userWallet.walletId)
|
||||
}
|
||||
|
||||
override suspend fun onTokenListReceived(maybeTokenList: Lce<TokenListError, TokenList>) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAn
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -34,6 +35,9 @@ internal class SingleWalletWithTokenListSubscriber(
|
|||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
) {
|
||||
|
||||
override fun tokenListFlow(): LceFlow<TokenListError, TokenList> = getNodlTokenListUseCase(userWallet.walletId)
|
||||
.map { it.toLce() }
|
||||
override fun tokenListFlow(coroutineScope: CoroutineScope): LceFlow<TokenListError, TokenList> =
|
||||
getNodlTokenListUseCase(
|
||||
userWallet.walletId,
|
||||
)
|
||||
.map { it.toLce() }
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import com.tangem.feature.wallet.presentation.deeplink.WalletDeepLinksHandler
|
|||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
|
||||
|
|
@ -69,6 +70,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
|
||||
private val marketsFeatureToggles: MarketsFeatureToggles,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
analyticsEventsHandler: AnalyticsEventHandler,
|
||||
) : ViewModel() {
|
||||
|
||||
|
|
@ -110,6 +112,8 @@ internal class WalletViewModel @Inject constructor(
|
|||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
|
||||
tokenListStore.clear()
|
||||
stateHolder.clear()
|
||||
walletScreenContentLoader.cancelAll()
|
||||
}
|
||||
|
|
@ -280,7 +284,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
stateHolder.update(transformer = RenameWalletTransformer(action.selectedWalletId, action.name))
|
||||
}
|
||||
is WalletsUpdateActionResolver.Action.Unknown -> {
|
||||
Timber.w("Unable to perfom action: $action")
|
||||
Timber.w("Unable to perform action: $action")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -318,6 +322,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
|
||||
private fun reinitializeWallet(action: WalletsUpdateActionResolver.Action.ReinitializeWallet) {
|
||||
walletScreenContentLoader.cancel(action.prevWalletId)
|
||||
tokenListStore.remove(action.prevWalletId)
|
||||
|
||||
walletScreenContentLoader.load(
|
||||
userWallet = action.selectedWallet,
|
||||
|
|
@ -357,6 +362,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
|
||||
private suspend fun deleteWallet(action: WalletsUpdateActionResolver.Action.DeleteWallet) {
|
||||
walletScreenContentLoader.cancel(action.deletedWalletId)
|
||||
tokenListStore.remove(action.deletedWalletId)
|
||||
|
||||
walletScreenContentLoader.load(
|
||||
userWallet = action.selectedWallet,
|
||||
|
|
|
|||
|
|
@ -136,7 +136,9 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
unlockedWallets = wallets.filterNot(UserWallet::isLocked),
|
||||
)
|
||||
}
|
||||
isSelectedWalletCardsCountChanged(state, selectedWallet) -> Action.UpdateWalletCardCount(selectedWallet)
|
||||
isSelectedWalletCardsCountChanged(state, selectedWallet) -> {
|
||||
Action.UpdateWalletCardCount(selectedWallet)
|
||||
}
|
||||
else -> Action.Unknown
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState
|
||||
|
|
@ -37,6 +38,7 @@ internal interface WalletCardClickIntents {
|
|||
@Suppress("LongParameterList")
|
||||
internal class WalletCardClickIntentsImplementor @Inject constructor(
|
||||
private val stateHolder: WalletStateController,
|
||||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
private val walletEventSender: WalletEventSender,
|
||||
private val walletScreenContentLoader: WalletScreenContentLoader,
|
||||
private val renameWalletUseCase: RenameWalletUseCase,
|
||||
|
|
@ -99,6 +101,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
|
|||
override fun onDeleteAfterConfirmationClick(userWalletId: UserWalletId) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
walletScreenContentLoader.cancel(userWalletId)
|
||||
tokenListStore.remove(userWalletId)
|
||||
|
||||
val walletToDelete = getUserWalletUseCase(userWalletId).getOrNull() ?: return@launch
|
||||
val hasUserWallets = deleteWalletUseCase(userWalletId).getOrElse {
|
||||
|
|
@ -117,6 +120,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
|
|||
|
||||
reduxStateHolder.onUserWalletSelected(selectedWallet)
|
||||
} else {
|
||||
tokenListStore.clear()
|
||||
stateHolder.clear()
|
||||
appRouter.replaceAll(AppRoute.Home)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.domain.settings.NeverToShowWalletsScrollPreview
|
|||
import com.tangem.domain.tokens.FetchCardTokenListUseCase
|
||||
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.FetchTokenListUseCase
|
||||
import com.tangem.domain.tokens.FetchTokenListUseCase.RefreshMode
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.domain.wallets.usecase.SelectWalletUseCase
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
|
|
@ -117,7 +118,7 @@ internal class WalletClickIntents @Inject constructor(
|
|||
val maybeFetchResult = if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
|
||||
fetchCardTokenListUseCase(userWalletId = userWallet.walletId, refresh = true)
|
||||
} else {
|
||||
fetchTokenListUseCase(userWalletId = userWallet.walletId, refresh = true)
|
||||
fetchTokenListUseCase(userWalletId = userWallet.walletId, mode = RefreshMode.FULL)
|
||||
}
|
||||
|
||||
maybeFetchResult.onLeft {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.tangem.domain.redux.LegacyAction
|
|||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.tokens.FetchTokenListUseCase
|
||||
import com.tangem.domain.tokens.FetchTokenListUseCase.RefreshMode
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
|
||||
|
|
@ -124,18 +125,20 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
analyticsEventHandler.send(Basic.CardWasScanned(AnalyticsParam.ScreensSources.Main))
|
||||
analyticsEventHandler.send(MainScreen.NoticeScanYourCardTapped)
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
viewModelScope.launch {
|
||||
val userWallet = getSelectedUserWallet() ?: return@launch
|
||||
|
||||
derivePublicKeysUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currencies = missedAddressCurrencies,
|
||||
).fold(
|
||||
ifLeft = { Timber.e(it, "Failed to derive public keys") },
|
||||
ifRight = {
|
||||
fetchTokenListUseCase(userWallet.walletId, mode = RefreshMode.SKIP_CURRENCIES).onLeft {
|
||||
Timber.e("Unable to refresh token list: $it")
|
||||
}
|
||||
},
|
||||
)
|
||||
.onRight {
|
||||
// Refresh must be set to true to ensure that yield balances are updated
|
||||
fetchTokenListUseCase(userWalletId = userWallet.walletId, refresh = true)
|
||||
}
|
||||
.onLeft { Timber.e("Failed to derive public keys: $it") }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue