Updated on 2026-08-14
This commit is contained in:
parent
428b2554fd
commit
0d185108da
14 changed files with 431 additions and 90 deletions
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.card.repository.DerivationsRepository
|
||||
import com.tangem.domain.markets.*
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -44,4 +46,14 @@ object MarketsDomainModule {
|
|||
fun provideGetTokenQuotesUseCase(quotesRepository: QuotesRepository): GetCurrencyQuotesUseCase {
|
||||
return GetCurrencyQuotesUseCase(quotesRepository = quotesRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSaveMarketTokensUseCase(
|
||||
derivationsRepository: DerivationsRepository,
|
||||
marketsTokenRepository: MarketsTokenRepository,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
): SaveMarketTokensUseCase {
|
||||
return SaveMarketTokensUseCase(derivationsRepository, marketsTokenRepository, currenciesRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,21 @@ internal class DefaultDerivationsRepository(
|
|||
derivePublicKeysByNetworks(userWalletId = userWalletId, networks = currencies.map(CryptoCurrency::network))
|
||||
}
|
||||
|
||||
override suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List<Network.ID>) {
|
||||
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
|
||||
|
||||
derivePublicKeysByNetworks(
|
||||
userWalletId = userWalletId,
|
||||
networks = networkIds.mapNotNull {
|
||||
getNetwork(
|
||||
blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun derivePublicKeysByNetworks(userWalletId: UserWalletId, networks: List<Network>) {
|
||||
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,16 @@ dependencies {
|
|||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.pagination)
|
||||
implementation(projects.domain.tokens.models)
|
||||
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.domain.markets)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
|
||||
implementation(projects.data.common)
|
||||
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
|
||||
// region DI
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
|
@ -28,7 +34,6 @@ dependencies {
|
|||
implementation(deps.moshi)
|
||||
implementation(deps.moshi.kotlin)
|
||||
implementation(deps.timber)
|
||||
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
implementation(deps.tangem.blockchain)
|
||||
// endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
package com.tangem.data.markets
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
import com.tangem.data.common.utils.retryOnError
|
||||
import com.tangem.data.markets.converters.TokenChartConverter
|
||||
import com.tangem.data.markets.converters.TokenMarketInfoConverter
|
||||
|
|
@ -8,8 +12,12 @@ import com.tangem.data.markets.converters.toRequestParam
|
|||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.markets.TangemTechMarketsApi
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.markets.*
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.pagination.*
|
||||
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -19,6 +27,7 @@ import java.util.concurrent.atomic.AtomicLong
|
|||
internal class DefaultMarketsTokenRepository(
|
||||
private val marketsApi: TangemTechMarketsApi,
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val dispatcherProvider: CoroutineDispatcherProvider,
|
||||
) : MarketsTokenRepository {
|
||||
|
||||
|
|
@ -148,4 +157,36 @@ internal class DefaultMarketsTokenRepository(
|
|||
|
||||
return TokenMarketInfoConverter.convert(response.getOrThrow()).quotes
|
||||
}
|
||||
|
||||
override suspend fun createCryptoCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
token: TokenMarketParams,
|
||||
network: TokenMarketInfo.Network,
|
||||
): CryptoCurrency? {
|
||||
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("UserWalletId [$userWalletId] not found")
|
||||
val blockchain = Blockchain.fromNetworkId(network.networkId) ?: error("Unknown network [${network.networkId}]")
|
||||
|
||||
return if (network.contractAddress == null) {
|
||||
CryptoCurrencyFactory().createCoin(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
)
|
||||
} else {
|
||||
val currencyNetwork = getNetwork(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
) ?: return null
|
||||
|
||||
CryptoCurrencyFactory().createToken(
|
||||
network = currencyNetwork,
|
||||
rawId = token.id,
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
decimals = network.decimalCount ?: error("Unknown decimal"),
|
||||
contractAddress = network.contractAddress!!,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.data.markets.DefaultMarketsTokenRepository
|
|||
import com.tangem.datasource.api.markets.TangemTechMarketsApi
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.di.DevTangemApi
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -21,12 +22,14 @@ internal object MarketsDataModule {
|
|||
fun provideMarketsRepository(
|
||||
@DevTangemApi marketsApi: TangemTechMarketsApi,
|
||||
@DevTangemApi tangemTechApi: TangemTechApi,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): MarketsTokenRepository {
|
||||
return DefaultMarketsTokenRepository(
|
||||
marketsApi = marketsApi,
|
||||
tangemTechApi = tangemTechApi,
|
||||
dispatcherProvider = dispatchers,
|
||||
userWalletsStore = userWalletsStore,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ interface DerivationsRepository {
|
|||
@Throws
|
||||
suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List<CryptoCurrency>)
|
||||
|
||||
suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List<Network.ID>)
|
||||
|
||||
@Throws
|
||||
suspend fun derivePublicKeysByNetworks(userWalletId: UserWalletId, networks: List<Network>)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,12 +13,16 @@ android {
|
|||
dependencies {
|
||||
/* Domain */
|
||||
api(projects.domain.appCurrency.models)
|
||||
api(projects.domain.card)
|
||||
api(projects.domain.core)
|
||||
api(projects.core.pagination)
|
||||
api(projects.domain.markets.models)
|
||||
api(projects.domain.wallets.models)
|
||||
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.tokens)
|
||||
|
||||
api(projects.core.pagination)
|
||||
|
||||
/* Utils */
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(projects.core.utils)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
package com.tangem.domain.markets
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.card.repository.DerivationsRepository
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
* Use case for saving tokens from Markets
|
||||
*
|
||||
* @property derivationsRepository derivations repository
|
||||
* @property marketsTokenRepository markets token repository
|
||||
* @property currenciesRepository currencies repository
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SaveMarketTokensUseCase(
|
||||
private val derivationsRepository: DerivationsRepository,
|
||||
private val marketsTokenRepository: MarketsTokenRepository,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
tokenMarketParams: TokenMarketParams,
|
||||
addedNetworks: Set<TokenMarketInfo.Network>,
|
||||
removedNetworks: Set<TokenMarketInfo.Network>,
|
||||
): Either<Throwable, Unit> = Either.catch {
|
||||
currenciesRepository.removeCurrencies(
|
||||
userWalletId = userWalletId,
|
||||
currencies = removedNetworks.mapNotNull {
|
||||
marketsTokenRepository.createCryptoCurrency(
|
||||
userWalletId = userWalletId,
|
||||
token = tokenMarketParams,
|
||||
network = it,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
derivationsRepository.derivePublicKeysByNetworkIds(
|
||||
userWalletId = userWalletId,
|
||||
networkIds = addedNetworks.map { Network.ID(it.networkId) },
|
||||
)
|
||||
|
||||
currenciesRepository.addCurrencies(
|
||||
userWalletId = userWalletId,
|
||||
currencies = addedNetworks.mapNotNull {
|
||||
marketsTokenRepository.createCryptoCurrency(
|
||||
userWalletId = userWalletId,
|
||||
token = tokenMarketParams,
|
||||
network = it,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
// TODO: [REDACTED_JIRA]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.domain.markets.repositories
|
||||
|
||||
import com.tangem.domain.markets.*
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface MarketsTokenRepository {
|
||||
|
||||
|
|
@ -17,4 +19,10 @@ interface MarketsTokenRepository {
|
|||
suspend fun getTokenInfo(fiatCurrencyCode: String, tokenId: String, languageCode: String): TokenMarketInfo
|
||||
|
||||
suspend fun getTokenQuotes(fiatCurrencyCode: String, tokenId: String): TokenQuotes
|
||||
|
||||
suspend fun createCryptoCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
token: TokenMarketParams,
|
||||
network: TokenMarketInfo.Network,
|
||||
): CryptoCurrency?
|
||||
}
|
||||
|
|
@ -30,7 +30,11 @@ internal class AddToPortfolioBSContentUMFactory(
|
|||
private val onWalletSelectorVisibilityChange: (Boolean) -> Unit,
|
||||
private val onNetworkSwitchClick: (String, Boolean) -> Unit,
|
||||
private val onWalletSelect: (UserWalletId) -> Unit,
|
||||
private val onContinueClick: () -> Unit,
|
||||
private val onContinueClick: (
|
||||
selectedWalletId: UserWalletId,
|
||||
addedNetworks: Set<TokenMarketInfo.Network>,
|
||||
removedNetworks: Set<TokenMarketInfo.Network>,
|
||||
) -> Unit,
|
||||
) {
|
||||
|
||||
/**
|
||||
|
|
@ -60,7 +64,23 @@ internal class AddToPortfolioBSContentUMFactory(
|
|||
).convert(value = token),
|
||||
isScanCardNotificationVisible = portfolioUIData.hasMissedDerivations,
|
||||
continueButtonEnabled = isUserChangedNetworks,
|
||||
onContinueButtonClick = onContinueClick,
|
||||
onContinueButtonClick = {
|
||||
val alreadyAddedNetworkIds = portfolioData.walletsWithCurrencies[selectedWallet].orEmpty()
|
||||
.map { it.status.currency.network.backendId }
|
||||
.toSet()
|
||||
|
||||
onContinueClick(
|
||||
selectedWallet.walletId,
|
||||
portfolioUIData.addToPortfolioData.getAddedNetworks(
|
||||
userWalletId = selectedWallet.walletId,
|
||||
alreadyAddedNetworkIds = alreadyAddedNetworkIds,
|
||||
),
|
||||
portfolioUIData.addToPortfolioData.getRemovedNetworks(
|
||||
userWalletId = selectedWallet.walletId,
|
||||
alreadyAddedNetworkIds = alreadyAddedNetworkIds,
|
||||
),
|
||||
)
|
||||
},
|
||||
walletSelectorConfig = crateWalletSelectorBSConfig(
|
||||
isShow = portfolioUIData.portfolioBSVisibilityModel.walletSelectorBSVisibility,
|
||||
portfolioData = portfolioData,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,188 @@
|
|||
package com.tangem.features.markets.portfolio.impl.model
|
||||
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.update
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
internal typealias WalletsWithNetworks = Map<UserWalletId, Set<TokenMarketInfo.Network>>
|
||||
|
||||
/**
|
||||
* Manager for tracking changing networks in AddToPortfolio
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Singleton
|
||||
internal class AddToPortfolioManager @Inject constructor() {
|
||||
|
||||
private val availableNetworks = MutableStateFlow<Set<TokenMarketInfo.Network>?>(value = null)
|
||||
private val addedNetworks = MutableStateFlow<WalletsWithNetworks>(value = emptyMap())
|
||||
private val removedNetworks = MutableStateFlow<WalletsWithNetworks>(value = emptyMap())
|
||||
|
||||
/** Get [AddToPortfolioData] as flow */
|
||||
fun getAddToPortfolioData(): Flow<AddToPortfolioData> {
|
||||
return combine(
|
||||
flow = availableNetworks,
|
||||
flow2 = addedNetworks,
|
||||
flow3 = removedNetworks,
|
||||
transform = ::AddToPortfolioData,
|
||||
)
|
||||
}
|
||||
|
||||
/** Set available networks [networks] */
|
||||
fun setAvailableNetworks(networks: List<TokenMarketInfo.Network>) {
|
||||
availableNetworks.value = networks.toSet()
|
||||
}
|
||||
|
||||
/** Add network [networkId] to [userWalletId] */
|
||||
fun addNetwork(userWalletId: UserWalletId, networkId: String) {
|
||||
addedNetworks.add(userWalletId, networkId)
|
||||
|
||||
removedNetworks.cancelPrevChangeIfExist(userWalletId = userWalletId, networkId = networkId)
|
||||
}
|
||||
|
||||
/** Remove network [networkId] from [userWalletId] */
|
||||
fun removeNetwork(userWalletId: UserWalletId, networkId: String) {
|
||||
removedNetworks.add(userWalletId, networkId)
|
||||
|
||||
addedNetworks.cancelPrevChangeIfExist(
|
||||
userWalletId = userWalletId,
|
||||
networkId = networkId,
|
||||
)
|
||||
}
|
||||
|
||||
/** Remove all networks by [userWalletId] */
|
||||
fun removeAllChanges(userWalletId: UserWalletId) {
|
||||
addedNetworks.update {
|
||||
it.toMutableMap().apply { remove(userWalletId) }
|
||||
}
|
||||
|
||||
removedNetworks.update {
|
||||
it.toMutableMap().apply { remove(userWalletId) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableStateFlow<WalletsWithNetworks>.cancelPrevChangeIfExist(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: String,
|
||||
) {
|
||||
if (value[userWalletId].orEmpty().any { it.networkId == networkId }) remove(userWalletId, networkId)
|
||||
}
|
||||
|
||||
private fun MutableStateFlow<WalletsWithNetworks>.add(userWalletId: UserWalletId, networkId: String) {
|
||||
change(userWalletId = userWalletId, networkId = networkId, isAddAction = true)
|
||||
}
|
||||
|
||||
private fun MutableStateFlow<WalletsWithNetworks>.remove(userWalletId: UserWalletId, networkId: String) {
|
||||
change(userWalletId = userWalletId, networkId = networkId, isAddAction = false)
|
||||
}
|
||||
|
||||
private fun MutableStateFlow<WalletsWithNetworks>.change(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: String,
|
||||
isAddAction: Boolean,
|
||||
) {
|
||||
val network = availableNetworks.value.orEmpty().firstOrNull { it.networkId == networkId }
|
||||
|
||||
if (network == null) {
|
||||
Timber.d(
|
||||
"Network [$networkId] doesn't contain in available networks [%s]",
|
||||
availableNetworks.value?.joinToString { it.networkId },
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
update {
|
||||
it.toMutableMap().apply {
|
||||
this[userWalletId] = if (isAddAction) {
|
||||
this[userWalletId].orEmpty() + network
|
||||
} else {
|
||||
this[userWalletId].orEmpty() - network
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to portfolio data
|
||||
*
|
||||
* @property availableNetworks available networks that user can add to portfolio
|
||||
* @property addedNetworks networks that user toggled on, but it might have already been added to the wallet
|
||||
* @property removedNetworks networks that user toggled off, but it might haven't been added to the wallet
|
||||
*
|
||||
* Example for [addedNetworks] and [removedNetworks]. This lists will include new networks when user just
|
||||
* toggle it. But when we will save user changes, we will check what tokens have already been added or
|
||||
* haven't been added to the wallet. See [getAddedNetworks] and [getRemovedNetworks]
|
||||
*/
|
||||
data class AddToPortfolioData(
|
||||
val availableNetworks: Set<TokenMarketInfo.Network>?,
|
||||
val addedNetworks: WalletsWithNetworks,
|
||||
private val removedNetworks: WalletsWithNetworks,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Associate network with toggle. If user changed toggle state then use it, otherwise check state by already
|
||||
* added networks.
|
||||
*
|
||||
* @param userWalletId user wallet id
|
||||
* @param alreadyAddedNetworkIds already added network ids
|
||||
*/
|
||||
fun associateWithToggle(
|
||||
userWalletId: UserWalletId,
|
||||
alreadyAddedNetworkIds: Set<String>,
|
||||
): Map<TokenMarketInfo.Network, Boolean> {
|
||||
// Use user choice or check already added networks
|
||||
return availableNetworks?.associateWith { availableNetwork ->
|
||||
val isAddedByUser = addedNetworks[userWalletId]?.contains(availableNetwork)
|
||||
|
||||
if (isAddedByUser == true) return@associateWith true
|
||||
|
||||
val isRemovedByUser = removedNetworks[userWalletId]?.contains(availableNetwork)
|
||||
|
||||
if (isRemovedByUser == true) return@associateWith false
|
||||
|
||||
val isAddedBefore = alreadyAddedNetworkIds.any { it == availableNetwork.networkId }
|
||||
|
||||
isAddedBefore
|
||||
}
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
fun isUserChangedNetworks(userWalletId: UserWalletId): Boolean {
|
||||
return addedNetworks[userWalletId].orEmpty().isNotEmpty() ||
|
||||
removedNetworks[userWalletId].orEmpty().isNotEmpty()
|
||||
}
|
||||
|
||||
/** Get new networks that user [userWalletId] added using [alreadyAddedNetworkIds] */
|
||||
fun getAddedNetworks(
|
||||
userWalletId: UserWalletId,
|
||||
alreadyAddedNetworkIds: Set<String>,
|
||||
): Set<TokenMarketInfo.Network> {
|
||||
val addedNetworksByUser = addedNetworks[userWalletId].orEmpty()
|
||||
|
||||
return addedNetworksByUser.map { it.networkId }
|
||||
.minus(alreadyAddedNetworkIds)
|
||||
.mapNotNull { networkId -> addedNetworksByUser.firstOrNull { it.networkId == networkId } }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
/** Get networks that user [userWalletId] removed using [alreadyAddedNetworkIds] */
|
||||
fun getRemovedNetworks(
|
||||
userWalletId: UserWalletId,
|
||||
alreadyAddedNetworkIds: Set<String>,
|
||||
): Set<TokenMarketInfo.Network> {
|
||||
val removedNetworksByUser = removedNetworks[userWalletId].orEmpty()
|
||||
|
||||
return alreadyAddedNetworkIds
|
||||
.minus(removedNetworksByUser.map { it.networkId }.toSet())
|
||||
.mapNotNull { networkId -> removedNetworksByUser.firstOrNull { it.networkId == networkId } }
|
||||
.toSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,10 @@ import arrow.core.getOrElse
|
|||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.card.HasMissedDerivationsUseCase
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.card.HasMissedDerivationsUseCase
|
||||
import com.tangem.domain.markets.SaveMarketTokensUseCase
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
|
@ -20,6 +21,7 @@ import com.tangem.utils.Provider
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -28,12 +30,14 @@ import javax.inject.Inject
|
|||
@ComponentScoped
|
||||
internal class MarketsPortfolioModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
tokenActionsIntentsFactory: TokenActionsHandler.Factory,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val portfolioDataLoader: PortfolioDataLoader,
|
||||
private val hasMissedDerivationsUseCase: HasMissedDerivationsUseCase,
|
||||
private val tokenActionsIntentsFactory: TokenActionsHandler.Factory,
|
||||
private val saveMarketTokensUseCase: SaveMarketTokensUseCase,
|
||||
private val addToPortfolioManager: AddToPortfolioManager,
|
||||
) : Model() {
|
||||
|
||||
val state: StateFlow<MyPortfolioUM> get() = _state
|
||||
|
|
@ -41,14 +45,9 @@ internal class MarketsPortfolioModel @Inject constructor(
|
|||
|
||||
private val params = paramsContainer.require<MarketsPortfolioComponent.Params>()
|
||||
|
||||
private val availableNetworksFlow = MutableStateFlow<List<TokenMarketInfo.Network>?>(value = null)
|
||||
|
||||
/** Multi-wallet [UserWalletId] that user uses to add new tokens in AddToPortfolio bottom sheet */
|
||||
private val selectedMultiWalletIdFlow = MutableStateFlow<UserWalletId?>(value = null)
|
||||
|
||||
/** Map of [UserWalletId] and network ids that user is changed */
|
||||
private val walletsWithChangedNetworksFlow = MutableStateFlow<Map<UserWalletId, List<String>>>(value = emptyMap())
|
||||
|
||||
private val portfolioBSVisibilityModelFlow = MutableStateFlow(value = PortfolioBSVisibilityModel())
|
||||
|
||||
private val currentAppCurrency = getSelectedAppCurrencyUseCase()
|
||||
|
|
@ -91,11 +90,11 @@ internal class MarketsPortfolioModel @Inject constructor(
|
|||
}
|
||||
|
||||
fun setTokenNetworks(networks: List<TokenMarketInfo.Network>) {
|
||||
availableNetworksFlow.value = networks
|
||||
addToPortfolioManager.setAvailableNetworks(networks)
|
||||
}
|
||||
|
||||
fun setNoNetworksAvailable() {
|
||||
availableNetworksFlow.value = emptyList()
|
||||
addToPortfolioManager.setAvailableNetworks(emptyList())
|
||||
}
|
||||
|
||||
private fun subscribeOnSelectedMultiWalletUpdates() {
|
||||
|
|
@ -114,7 +113,6 @@ internal class MarketsPortfolioModel @Inject constructor(
|
|||
combine(
|
||||
flow = portfolioDataLoader.load(params.token.id),
|
||||
flow2 = getPortfolioUIDataFlow(),
|
||||
flow3 = availableNetworksFlow,
|
||||
transform = factory::create,
|
||||
)
|
||||
.onEach { _state.value = it }
|
||||
|
|
@ -125,13 +123,13 @@ internal class MarketsPortfolioModel @Inject constructor(
|
|||
return combine(
|
||||
flow = portfolioBSVisibilityModelFlow,
|
||||
flow2 = selectedMultiWalletIdFlow,
|
||||
flow3 = walletsWithChangedNetworksFlow,
|
||||
transform = { portfolioBSVisibilityModel, selectedWalletId, walletsWithChangedNetworks ->
|
||||
flow3 = addToPortfolioManager.getAddToPortfolioData(),
|
||||
transform = { portfolioBSVisibilityModel, selectedWalletId, addToPortfolioData ->
|
||||
PortfolioUIData(
|
||||
portfolioBSVisibilityModel = portfolioBSVisibilityModel,
|
||||
selectedWalletId = selectedWalletId,
|
||||
walletsWithChangedNetworks = walletsWithChangedNetworks,
|
||||
hasMissedDerivations = hasMissedDerivations(selectedWalletId, walletsWithChangedNetworks),
|
||||
addToPortfolioData = addToPortfolioData,
|
||||
hasMissedDerivations = hasMissedDerivations(selectedWalletId, addToPortfolioData),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
@ -139,13 +137,13 @@ internal class MarketsPortfolioModel @Inject constructor(
|
|||
|
||||
private suspend fun hasMissedDerivations(
|
||||
selectedWalletId: UserWalletId?,
|
||||
walletsWithChangedNetworks: Map<UserWalletId, List<String>>,
|
||||
addToPortfolioData: AddToPortfolioManager.AddToPortfolioData,
|
||||
): Boolean {
|
||||
return if (selectedWalletId != null) {
|
||||
hasMissedDerivationsUseCase.invoke(
|
||||
userWalletId = selectedWalletId,
|
||||
networksWithDerivationPath = walletsWithChangedNetworks[selectedWalletId].orEmpty()
|
||||
.associate { Network.ID(it) to null },
|
||||
networksWithDerivationPath = addToPortfolioData.addedNetworks[selectedWalletId].orEmpty()
|
||||
.associate { Network.ID(it.networkId) to null },
|
||||
)
|
||||
} else {
|
||||
false
|
||||
|
|
@ -169,41 +167,46 @@ internal class MarketsPortfolioModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun onNetworkSwitchClick(id: String, isChecked: Boolean) {
|
||||
walletsWithChangedNetworksFlow.update {
|
||||
val selectedWalletId = selectedMultiWalletIdFlow.value
|
||||
private fun onNetworkSwitchClick(networkId: String, isChecked: Boolean) {
|
||||
val selectedWalletId = selectedMultiWalletIdFlow.value
|
||||
|
||||
if (selectedWalletId == null) {
|
||||
Timber.e("Impossible ti switch network when selected wallet is null")
|
||||
return@update it
|
||||
}
|
||||
if (selectedWalletId == null) {
|
||||
Timber.e("Impossible to switch network when selected wallet is null")
|
||||
return
|
||||
}
|
||||
|
||||
it.toMutableMap().apply {
|
||||
val networkIds = this[selectedWalletId] ?: emptyList()
|
||||
|
||||
if (isChecked) {
|
||||
this[selectedWalletId] = networkIds + id
|
||||
} else {
|
||||
this[selectedWalletId] = networkIds - id
|
||||
}
|
||||
}
|
||||
if (isChecked) {
|
||||
addToPortfolioManager.addNetwork(userWalletId = selectedWalletId, networkId = networkId)
|
||||
} else {
|
||||
addToPortfolioManager.removeNetwork(userWalletId = selectedWalletId, networkId = networkId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onWalletSelect(userWalletId: UserWalletId) {
|
||||
selectedMultiWalletIdFlow.update { prevUserWalletId ->
|
||||
|
||||
// Clear user changes if user select another wallet
|
||||
walletsWithChangedNetworksFlow.update {
|
||||
it.toMutableMap().apply { remove(prevUserWalletId) }
|
||||
}
|
||||
prevUserWalletId?.let(addToPortfolioManager::removeAllChanges)
|
||||
|
||||
userWalletId
|
||||
}
|
||||
}
|
||||
|
||||
private fun onContinueClick() {
|
||||
// TODO [REDACTED_JIRA]
|
||||
private fun onContinueClick(
|
||||
userWalletId: UserWalletId,
|
||||
addedNetworks: Set<TokenMarketInfo.Network>,
|
||||
removedNetworks: Set<TokenMarketInfo.Network>,
|
||||
) {
|
||||
modelScope.launch {
|
||||
saveMarketTokensUseCase(
|
||||
userWalletId = userWalletId,
|
||||
tokenMarketParams = params.token,
|
||||
addedNetworks = addedNetworks,
|
||||
removedNetworks = removedNetworks,
|
||||
)
|
||||
|
||||
onAddToPortfolioBSVisibilityChange(isShow = false)
|
||||
|
||||
addToPortfolioManager.removeAllChanges(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAddToPortfolioBSVisibilityChange(isShow: Boolean) {
|
||||
|
|
|
|||
|
|
@ -23,17 +23,15 @@ internal class MyPortfolioUMFactory(
|
|||
private val tokenActionsHandler: TokenActionsHandler,
|
||||
) {
|
||||
|
||||
fun create(
|
||||
portfolioData: PortfolioData,
|
||||
portfolioUIData: PortfolioUIData,
|
||||
availableNetworks: List<TokenMarketInfo.Network>?,
|
||||
): MyPortfolioUM {
|
||||
if (availableNetworks == null) return MyPortfolioUM.Loading
|
||||
fun create(portfolioData: PortfolioData, portfolioUIData: PortfolioUIData): MyPortfolioUM {
|
||||
val addToPortfolioData = portfolioUIData.addToPortfolioData
|
||||
|
||||
if (availableNetworks.isEmpty()) return MyPortfolioUM.Unavailable
|
||||
if (addToPortfolioData.availableNetworks == null) return MyPortfolioUM.Loading
|
||||
|
||||
if (addToPortfolioData.availableNetworks.isEmpty()) return MyPortfolioUM.Unavailable
|
||||
|
||||
val walletsWithCurrencies = portfolioData.walletsWithCurrencies
|
||||
.filterAvailableNetworks(networks = availableNetworks)
|
||||
.filterAvailableNetworks(networks = addToPortfolioData.availableNetworks)
|
||||
|
||||
val isPortfolioEmpty = walletsWithCurrencies.flatMap { it.value }.isEmpty()
|
||||
if (isPortfolioEmpty) {
|
||||
|
|
@ -44,7 +42,6 @@ internal class MyPortfolioUMFactory(
|
|||
addToPortfolioBSConfig = createAddToPortfolioBSConfig(
|
||||
portfolioData = portfolioData,
|
||||
portfolioUIData = portfolioUIData,
|
||||
availableNetworks = availableNetworks,
|
||||
),
|
||||
onAddClick = onAddClick,
|
||||
)
|
||||
|
|
@ -56,12 +53,10 @@ internal class MyPortfolioUMFactory(
|
|||
return TokensPortfolioUMConverter(
|
||||
appCurrency = portfolioData.appCurrency,
|
||||
isBalanceHidden = portfolioData.isBalanceHidden,
|
||||
isAllAvailableNetworksAdded = walletsWithCurrencies.isAllAvailableNetworksAdded(availableNetworks),
|
||||
bsConfig = createAddToPortfolioBSConfig(
|
||||
portfolioData = portfolioData,
|
||||
portfolioUIData = portfolioUIData,
|
||||
availableNetworks = availableNetworks,
|
||||
isAllAvailableNetworksAdded = walletsWithCurrencies.isAllAvailableNetworksAdded(
|
||||
availableNetworks = addToPortfolioData.availableNetworks,
|
||||
),
|
||||
bsConfig = createAddToPortfolioBSConfig(portfolioData = portfolioData, portfolioUIData = portfolioUIData),
|
||||
onAddClick = onAddClick,
|
||||
onTokenItemClick = onTokenItemClick,
|
||||
quickActionsIntents = tokenActionsHandler,
|
||||
|
|
@ -72,7 +67,6 @@ internal class MyPortfolioUMFactory(
|
|||
private fun createAddToPortfolioBSConfig(
|
||||
portfolioData: PortfolioData,
|
||||
portfolioUIData: PortfolioUIData,
|
||||
availableNetworks: List<TokenMarketInfo.Network>,
|
||||
): TangemBottomSheetConfig {
|
||||
val walletId = portfolioUIData.selectedWalletId
|
||||
?: portfolioData.walletsWithCurrencies.keys.firstOrNull { it.isMultiCurrency }?.walletId
|
||||
|
|
@ -81,46 +75,32 @@ internal class MyPortfolioUMFactory(
|
|||
.firstOrNull { it.walletId == walletId }
|
||||
?: error("portfolioModel.walletsWithCurrencyStatuses doesn't contain selected wallet: $walletId")
|
||||
|
||||
val changedNetworks = portfolioUIData.walletsWithChangedNetworks[portfolioUIData.selectedWalletId]
|
||||
val availableNetworks = portfolioUIData.addToPortfolioData.availableNetworks.orEmpty()
|
||||
|
||||
val alreadyAddedNetworks = requireNotNull(
|
||||
value = portfolioData.walletsWithCurrencies[selectedWallet],
|
||||
value = portfolioData.walletsWithCurrencies
|
||||
.filterAvailableNetworks(availableNetworks)[selectedWallet],
|
||||
lazyMessage = {
|
||||
"portfolioModel.walletsWithCurrencyStatuses doesn't contain selected wallet: $walletId"
|
||||
},
|
||||
)
|
||||
.filterAvailableNetworks(availableNetworks)
|
||||
.map { it.status.currency.network.backendId }
|
||||
.toSet()
|
||||
|
||||
return addToPortfolioBSContentUMFactory.create(
|
||||
portfolioData = portfolioData,
|
||||
portfolioUIData = portfolioUIData,
|
||||
selectedWallet = selectedWallet,
|
||||
networksWithToggle = availableNetworks.associateWithToggle(
|
||||
changedNetworks = changedNetworks,
|
||||
alreadyAddedNetworks = alreadyAddedNetworks,
|
||||
networksWithToggle = portfolioUIData.addToPortfolioData.associateWithToggle(
|
||||
userWalletId = selectedWallet.walletId,
|
||||
alreadyAddedNetworkIds = alreadyAddedNetworks,
|
||||
),
|
||||
isUserChangedNetworks = changedNetworks != null && alreadyAddedNetworks != changedNetworks,
|
||||
isUserChangedNetworks = portfolioUIData.addToPortfolioData.isUserChangedNetworks(selectedWallet.walletId),
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<TokenMarketInfo.Network>.associateWithToggle(
|
||||
changedNetworks: List<String>?,
|
||||
alreadyAddedNetworks: List<String>,
|
||||
): Map<TokenMarketInfo.Network, Boolean> {
|
||||
// Use user choice or check already added networks
|
||||
return associateWith { network ->
|
||||
val isSelectedByUser = changedNetworks?.contains(network.networkId)
|
||||
|
||||
if (isSelectedByUser != null) return@associateWith isSelectedByUser
|
||||
|
||||
val isAlreadyAdded = alreadyAddedNetworks.any { it == network.networkId }
|
||||
|
||||
isAlreadyAdded
|
||||
}
|
||||
}
|
||||
|
||||
private fun Map<UserWallet, List<PortfolioData.CryptoCurrencyData>>.isAllAvailableNetworksAdded(
|
||||
availableNetworks: List<TokenMarketInfo.Network>,
|
||||
availableNetworks: Set<TokenMarketInfo.Network>,
|
||||
): Boolean {
|
||||
val networkIds = availableNetworks.map { it.networkId }
|
||||
|
||||
|
|
@ -134,14 +114,14 @@ internal class MyPortfolioUMFactory(
|
|||
|
||||
/** Filter map values by available networks [networks] */
|
||||
private fun Map<UserWallet, List<PortfolioData.CryptoCurrencyData>>.filterAvailableNetworks(
|
||||
networks: List<TokenMarketInfo.Network>,
|
||||
networks: Set<TokenMarketInfo.Network>,
|
||||
): Map<UserWallet, List<PortfolioData.CryptoCurrencyData>> {
|
||||
return mapValues { entry -> entry.value.filterAvailableNetworks(networks) }
|
||||
}
|
||||
|
||||
/** Filter list of [CryptoCurrencyStatus] by available networks [networks] */
|
||||
private fun List<PortfolioData.CryptoCurrencyData>.filterAvailableNetworks(
|
||||
networks: List<TokenMarketInfo.Network>,
|
||||
networks: Set<TokenMarketInfo.Network>,
|
||||
): List<PortfolioData.CryptoCurrencyData> {
|
||||
val networkIds = networks.map(TokenMarketInfo.Network::networkId)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
*
|
||||
* @property portfolioBSVisibilityModel portfolio bottom sheet visibility model
|
||||
* @property selectedWalletId selected wallet id
|
||||
* @property walletsWithChangedNetworks wallets with changed networks
|
||||
* @property addToPortfolioData add to portfolio data
|
||||
* @property hasMissedDerivations flag that indicates if user has missed derivations
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -15,6 +15,6 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
internal data class PortfolioUIData(
|
||||
val portfolioBSVisibilityModel: PortfolioBSVisibilityModel,
|
||||
val selectedWalletId: UserWalletId?,
|
||||
val walletsWithChangedNetworks: Map<UserWalletId, List<String>>,
|
||||
val addToPortfolioData: AddToPortfolioManager.AddToPortfolioData,
|
||||
val hasMissedDerivations: Boolean,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue