Updated on 2026-08-14

This commit is contained in:
Tangem 2024-09-03 13:25:28 +04:00
parent 428b2554fd
commit 0d185108da
14 changed files with 431 additions and 90 deletions

View file

@ -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)
}
}

View file

@ -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")

View file

@ -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
}

View file

@ -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!!,
)
}
}
}

View file

@ -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,
)
}
}

View file

@ -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>)

View file

@ -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)

View file

@ -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]
}
}

View file

@ -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?
}

View file

@ -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,

View file

@ -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()
}
}
}

View file

@ -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) {

View file

@ -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)

View file

@ -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,
)