Updated on 2026-08-14
This commit is contained in:
parent
98b8c6b58e
commit
17d7b65e51
7 changed files with 108 additions and 63 deletions
|
|
@ -25,6 +25,7 @@ dependencies {
|
|||
|
||||
/* Domain */
|
||||
implementation(projects.domain.account)
|
||||
implementation(projects.domain.account.status)
|
||||
implementation(projects.domain.appCurrency)
|
||||
implementation(projects.domain.appCurrency.models)
|
||||
implementation(projects.domain.balanceHiding)
|
||||
|
|
|
|||
|
|
@ -4,40 +4,51 @@ import com.tangem.domain.markets.TokenMarketInfo
|
|||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
data class AvailableToAddData(
|
||||
internal data class AvailableToAddData(
|
||||
val availableToAddWallets: Map<UserWalletId, AvailableToAddWallet>,
|
||||
) {
|
||||
val availableToAdd: Boolean
|
||||
get() = availableToAddWallets.isNotEmpty()
|
||||
val isSinglePortfolio: Boolean
|
||||
get() = availableToAddWallets.size == 1 && availableToAddWallets.values.first().accounts.size == 1
|
||||
}
|
||||
|
||||
data class AvailableToAddWallet(
|
||||
internal data class AvailableToAddWallet(
|
||||
val userWallet: UserWallet,
|
||||
val accounts: Set<AccountStatus>,
|
||||
val availableNetworks: Set<TokenMarketInfo.Network>,
|
||||
val availableToAddAccounts: Map<AccountId, AvailableToAddAccount>,
|
||||
) {
|
||||
val isSingleAvailableNetworks: Boolean get() = availableNetworks.size == 1
|
||||
val singleAvailableNetworks: TokenMarketInfo.Network get() = availableNetworks.first()
|
||||
}
|
||||
|
||||
data class AvailableToAddAccount(
|
||||
val account: AccountStatus,
|
||||
val availableNetworks: Set<TokenMarketInfo.Network>,
|
||||
val availableToAddNetworks: Set<TokenMarketInfo.Network>,
|
||||
)
|
||||
|
||||
data class SelectedPortfolio(
|
||||
val userWallet: UserWallet,
|
||||
internal data class AvailableToAddAccount(
|
||||
val account: AccountStatus,
|
||||
val availableNetworks: Set<TokenMarketInfo.Network>,
|
||||
val addedNetworks: Set<Network>,
|
||||
) {
|
||||
val isSingleNetwork: Boolean
|
||||
get() = availableNetworks.size == 1
|
||||
|
||||
val availableToAddNetworks: Set<TokenMarketInfo.Network> = availableNetworks
|
||||
.filter { available -> addedNetworks.none { added -> added.backendId == available.networkId } }
|
||||
.toSet()
|
||||
|
||||
val addedMarketNetworks: Set<TokenMarketInfo.Network> = availableNetworks
|
||||
.filter { available -> addedNetworks.any { added -> added.backendId == available.networkId } }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
internal data class SelectedPortfolio(
|
||||
val userWallet: UserWallet,
|
||||
val account: AvailableToAddAccount,
|
||||
val isAccountMode: Boolean,
|
||||
val availableMorePortfolio: Boolean,
|
||||
)
|
||||
|
||||
data class SelectedNetwork(
|
||||
internal data class SelectedNetwork(
|
||||
val selectedNetwork: TokenMarketInfo.Network,
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
val availableMoreNetwork: Boolean,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.markets.portfolio.add.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
|
|
@ -8,6 +9,7 @@ import com.tangem.core.decompose.factory.ComponentFactory
|
|||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.markets.portfolio.add.api.SelectedNetwork
|
||||
import com.tangem.features.markets.portfolio.add.api.SelectedPortfolio
|
||||
import com.tangem.features.markets.portfolio.add.impl.model.AddTokenModel
|
||||
|
|
@ -46,7 +48,7 @@ internal class AddTokenComponent @AssistedInject constructor(
|
|||
interface Callbacks {
|
||||
fun onChangeNetworkClick()
|
||||
fun onChangePortfolioClick()
|
||||
fun onTokenAdded()
|
||||
fun onTokenAdded(status: CryptoCurrencyStatus)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
package com.tangem.features.markets.portfolio.add.impl.converter
|
||||
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.markets.FilterAvailableNetworksForWalletUseCase
|
||||
import com.tangem.domain.markets.GetTokenMarketCryptoCurrency
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
|
|
@ -12,33 +16,42 @@ import com.tangem.features.markets.portfolio.add.api.AvailableToAddData
|
|||
import com.tangem.features.markets.portfolio.add.api.AvailableToAddWallet
|
||||
import javax.inject.Inject
|
||||
|
||||
class AvailableToAddDataConverter @Inject constructor(
|
||||
internal class AvailableToAddDataConverter @Inject constructor(
|
||||
private val filterAvailableNetworksForWalletUseCase: FilterAvailableNetworksForWalletUseCase,
|
||||
private val getTokenMarketCryptoCurrency: GetTokenMarketCryptoCurrency,
|
||||
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
|
||||
) {
|
||||
|
||||
suspend fun convert(
|
||||
balances: Map<UserWallet, PortfolioFetcher.PortfolioBalance>,
|
||||
availableNetworks: Set<TokenMarketInfo.Network>,
|
||||
marketParams: TokenMarketParams,
|
||||
): AvailableToAddData {
|
||||
fun AccountStatus.getAvailableToAddAccount(): AvailableToAddAccount {
|
||||
val availableToAddNetworks = availableNetworks
|
||||
.filterTo(mutableSetOf()) { network -> !this.isAlreadyAddedNetworks(network) }
|
||||
suspend fun AccountStatus.getAvailableToAddAccount(wallet: UserWallet): AvailableToAddAccount {
|
||||
val addedNetworks = availableNetworks
|
||||
.mapNotNull { createCryptoCurrency(wallet, it, marketParams) }
|
||||
.mapNotNull { getAccountCurrencyStatusUseCase.invokeSync(wallet.walletId, it) }
|
||||
.mapNotNull { it.getOrNull()?.status?.currency?.network }
|
||||
.toSet()
|
||||
return AvailableToAddAccount(
|
||||
account = this,
|
||||
availableNetworks = availableNetworks,
|
||||
availableToAddNetworks = availableToAddNetworks,
|
||||
addedNetworks = addedNetworks,
|
||||
)
|
||||
}
|
||||
|
||||
fun UserWallet.getAvailableToAddWallet(balance: PortfolioFetcher.PortfolioBalance): AvailableToAddWallet {
|
||||
val filteredNetworks = this.filteredAvailableNetworks(availableNetworks)
|
||||
suspend fun getAvailableToAddWallet(
|
||||
entry: Map.Entry<UserWallet, PortfolioFetcher.PortfolioBalance>,
|
||||
): AvailableToAddWallet {
|
||||
val (wallet, balance) = entry
|
||||
val filteredNetworks = wallet.filteredAvailableNetworks(availableNetworks)
|
||||
val accounts = balance.accountsBalance.accountStatuses
|
||||
val availableToAddAccounts: Map<AccountId, AvailableToAddAccount> = accounts
|
||||
.map { it.account.accountId to it.getAvailableToAddAccount() }
|
||||
.map { it.account.accountId to it.getAvailableToAddAccount(wallet) }
|
||||
.filter { (_, account) -> account.availableToAddNetworks.isNotEmpty() }
|
||||
.toMap()
|
||||
return AvailableToAddWallet(
|
||||
userWallet = this,
|
||||
userWallet = wallet,
|
||||
accounts = accounts,
|
||||
availableNetworks = filteredNetworks,
|
||||
availableToAddAccounts = availableToAddAccounts,
|
||||
|
|
@ -46,8 +59,9 @@ class AvailableToAddDataConverter @Inject constructor(
|
|||
}
|
||||
|
||||
val availableToAddWallets: Map<UserWalletId, AvailableToAddWallet> = balances
|
||||
.map { (wallet, balance) ->
|
||||
val availableToAddWallet = wallet.getAvailableToAddWallet(balance)
|
||||
.map {
|
||||
val (wallet, balance) = it
|
||||
val availableToAddWallet = getAvailableToAddWallet(it)
|
||||
wallet.walletId to availableToAddWallet
|
||||
}
|
||||
.filter { (_, wallet) -> wallet.availableToAddAccounts.isNotEmpty() }
|
||||
|
|
@ -64,8 +78,13 @@ class AvailableToAddDataConverter @Inject constructor(
|
|||
networks = networks,
|
||||
)
|
||||
|
||||
private fun AccountStatus.isAlreadyAddedNetworks(network: TokenMarketInfo.Network): Boolean = when (this) {
|
||||
is AccountStatus.CryptoPortfolio -> this.tokenList.flattenCurrencies()
|
||||
.any { status -> network.networkId == status.currency.network.backendId }
|
||||
}
|
||||
private suspend fun createCryptoCurrency(
|
||||
userWallet: UserWallet,
|
||||
network: TokenMarketInfo.Network,
|
||||
marketParams: TokenMarketParams,
|
||||
): CryptoCurrency? = getTokenMarketCryptoCurrency(
|
||||
userWalletId = userWallet.walletId,
|
||||
tokenMarketParams = marketParams,
|
||||
network = network,
|
||||
)
|
||||
}
|
||||
|
|
@ -4,7 +4,8 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.markets.SaveMarketTokensUseCase
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.account.status.usecase.SaveCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.wallets.usecase.ColdWalletAndHasMissedDerivationsUseCase
|
||||
import com.tangem.features.markets.portfolio.add.api.SelectedNetwork
|
||||
import com.tangem.features.markets.portfolio.add.api.SelectedPortfolio
|
||||
|
|
@ -20,13 +21,15 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
internal class AddTokenModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
private val saveMarketTokensUseCase: SaveMarketTokensUseCase,
|
||||
private val uiBuilder: AddTokenUiBuilder,
|
||||
private val coldWalletAndHasMissedDerivationsUseCase: ColdWalletAndHasMissedDerivationsUseCase,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val saveCryptoCurrenciesUseCase: SaveCryptoCurrenciesUseCase,
|
||||
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<AddTokenComponent.Params>()
|
||||
|
|
@ -37,13 +40,10 @@ internal class AddTokenModel @Inject constructor(
|
|||
field = MutableStateFlow(value = null)
|
||||
|
||||
init {
|
||||
channelFlow {
|
||||
val selectedNetwork = params.selectedNetwork.stateIn(this)
|
||||
val selectedPortfolio = params.selectedPortfolio.stateIn(this)
|
||||
combine(
|
||||
flow = selectedNetwork,
|
||||
flow2 = selectedPortfolio,
|
||||
) { selectedNetwork, selectedPortfolio ->
|
||||
combine(
|
||||
flow = params.selectedNetwork.distinctUntilChanged(),
|
||||
flow2 = params.selectedPortfolio.distinctUntilChanged(),
|
||||
transform = { selectedNetwork, selectedPortfolio ->
|
||||
addTokenJob.cancel()
|
||||
val isTangemIconVisible = needColdWalletInteraction(selectedNetwork, selectedPortfolio)
|
||||
uiBuilder.updateContent(
|
||||
|
|
@ -52,29 +52,35 @@ internal class AddTokenModel @Inject constructor(
|
|||
isTangemIconVisible = isTangemIconVisible,
|
||||
onConfirmClick = { onAddClick(selectedNetwork, selectedPortfolio).saveIn(addTokenJob) },
|
||||
)
|
||||
}.collect { newUI -> send(newUI) }
|
||||
}
|
||||
.flowOn(dispatchers.default)
|
||||
},
|
||||
)
|
||||
.onEach { newUI -> uiState.value = newUI }
|
||||
.flowOn(dispatchers.default)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun onAddClick(selectedNetwork: SelectedNetwork, selectedPortfolio: SelectedPortfolio) = modelScope.launch {
|
||||
val um = uiState.value ?: return@launch
|
||||
uiState.value = um.toggleProgress(true)
|
||||
val blockchainNames = listOf(selectedNetwork.selectedNetwork)
|
||||
.mapNotNull { BlockchainUtils.getNetworkInfo(it.networkId)?.name }
|
||||
analyticsEventHandler.send(analyticsEventBuilder.addToPortfolioContinue(blockchainNames))
|
||||
// todo account
|
||||
saveMarketTokensUseCase(
|
||||
userWalletId = selectedPortfolio.userWallet.walletId,
|
||||
tokenMarketParams = params.marketParams,
|
||||
addedNetworks = setOf(selectedNetwork.selectedNetwork),
|
||||
removedNetworks = setOf(),
|
||||
)
|
||||
params.callbacks.onTokenAdded()
|
||||
uiState.value = um.toggleProgress(false)
|
||||
}
|
||||
private fun onAddClick(selectedNetwork: SelectedNetwork, selectedPortfolio: SelectedPortfolio) =
|
||||
modelScope.launch(dispatchers.default) {
|
||||
val um = uiState.value ?: return@launch
|
||||
uiState.value = um.toggleProgress(true)
|
||||
val blockchainNames = listOf(selectedNetwork.selectedNetwork)
|
||||
.mapNotNull { BlockchainUtils.getNetworkInfo(it.networkId)?.name }
|
||||
analyticsEventHandler.send(analyticsEventBuilder.addToPortfolioContinue(blockchainNames))
|
||||
val cryptoCurrency = selectedNetwork.cryptoCurrency
|
||||
val accountId = selectedPortfolio.account.account.account.accountId
|
||||
saveCryptoCurrenciesUseCase(
|
||||
accountId = accountId,
|
||||
add = listOf(cryptoCurrency),
|
||||
remove = listOf(),
|
||||
)
|
||||
val status = getAccountCurrencyStatusUseCase.invokeSync(
|
||||
userWalletId = accountId.userWalletId,
|
||||
currencyId = cryptoCurrency.id,
|
||||
network = cryptoCurrency.network,
|
||||
).getOrNull() ?: return@launch
|
||||
params.callbacks.onTokenAdded(status.status)
|
||||
uiState.value = um.toggleProgress(false)
|
||||
}
|
||||
|
||||
private suspend fun needColdWalletInteraction(
|
||||
selectedNetwork: SelectedNetwork,
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@ import com.tangem.features.markets.portfolio.add.api.SelectedNetwork
|
|||
import com.tangem.features.markets.portfolio.add.api.SelectedPortfolio
|
||||
import com.tangem.features.markets.portfolio.add.impl.AddTokenComponent
|
||||
import com.tangem.features.markets.portfolio.add.impl.ui.state.AddTokenUM
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class AddTokenUiBuilder(
|
||||
internal class AddTokenUiBuilder @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
) {
|
||||
private val params = paramsContainer.require<AddTokenComponent.Params>()
|
||||
|
|
@ -41,9 +42,10 @@ internal class AddTokenUiBuilder(
|
|||
portfolioName = stringReference(selectedPortfolio.userWallet.name)
|
||||
}
|
||||
true -> {
|
||||
portfolioName = selectedPortfolio.account.account.accountName.toUM().value
|
||||
accountIcon = when (selectedPortfolio.account) {
|
||||
is AccountStatus.CryptoPortfolio -> selectedPortfolio.account.account.icon.toUM()
|
||||
val accountStatus = selectedPortfolio.account.account
|
||||
portfolioName = accountStatus.account.accountName.toUM().value
|
||||
accountIcon = when (accountStatus) {
|
||||
is AccountStatus.CryptoPortfolio -> accountStatus.account.icon.toUM()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,8 +63,11 @@ internal class AddTokenUiBuilder(
|
|||
isTangemIconVisible: Boolean,
|
||||
onConfirmClick: () -> Unit,
|
||||
): AddTokenUM {
|
||||
// its may happens when change portfolio after selected both params in line navigation
|
||||
val isAvailableNetwork = selectedPortfolio.account.availableToAddNetworks
|
||||
.any { selectedNetwork.selectedNetwork.networkId == it.networkId }
|
||||
val button = AddTokenUM.Button(
|
||||
isEnabled = true,
|
||||
isEnabled = isAvailableNetwork,
|
||||
showProgress = false,
|
||||
isTangemIconVisible = isTangemIconVisible,
|
||||
text = resourceReference(R.string.common_add),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue