Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-20 12:47:25 +03:00
commit c50840894f
739 changed files with 27664 additions and 4701 deletions

View file

@ -50,7 +50,7 @@ internal class OnrampAddTokenModel @Inject constructor(
.distinctUntilChanged()
.mapLatest { tokenToAdd: AddHotCryptoData ->
addTokenJob.join()
val backendId = tokenToAdd.cryptoCurrency.network.backendId
val backendId = tokenToAdd.cryptoCurrency.network.rawId
val userWalletId = tokenToAdd.account.accountId.userWalletId
val isTangemIconVisible = needColdWalletInteraction(
walletId = userWalletId,

View file

@ -15,7 +15,7 @@ import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioComponent
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
import com.tangem.features.onramp.component.SwapSelectTokensComponent
import com.tangem.features.onramp.swap.availablepairs.AvailableSwapPairsComponent
import com.tangem.features.onramp.swap.availablepairs.model.AddToPortfolioRoute
@ -74,9 +74,7 @@ internal class DefaultSwapSelectTokensComponent @AssistedInject constructor(
return addToPortfolioComponentFactory.create(
context = childByContext(componentContext),
params = AddToPortfolioComponent.Params(
addToPortfolioManager = selectToTokenListComponent.addToPortfolioManager!!,
callback = selectToTokenListComponent.addToPortfolioCallback,
shouldSkipTokenActionsScreen = true,
addToPortfolioManager = selectToTokenListComponent.addToPortfolioManager,
),
)
}

View file

@ -7,8 +7,7 @@ import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.decompose.ComposableListContentComponent
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioComponent
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioManager
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.onramp.swap.availablepairs.model.AddToPortfolioRoute
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
import kotlinx.coroutines.flow.StateFlow
@ -18,8 +17,7 @@ import kotlinx.coroutines.flow.StateFlow
internal interface AvailableSwapPairsComponent : ComposableListContentComponent<TokenListUM> {
val bottomSheetNavigation: SlotNavigation<AddToPortfolioRoute>
val addToPortfolioManager: AddToPortfolioManager?
val addToPortfolioCallback: AddToPortfolioComponent.Callback
val addToPortfolioManager: AddToPortfolioManager
/** Component factory */
interface Factory : ComponentFactory<Params, AvailableSwapPairsComponent>

View file

@ -6,8 +6,7 @@ import androidx.compose.ui.Modifier
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioComponent
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioManager
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.onramp.swap.availablepairs.model.AddToPortfolioRoute
import com.tangem.features.onramp.swap.availablepairs.model.AvailableSwapPairsModel
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
@ -26,8 +25,7 @@ internal class DefaultAvailableSwapPairsComponent @AssistedInject constructor(
private val model: AvailableSwapPairsModel = getOrCreateModel(params)
override val bottomSheetNavigation: SlotNavigation<AddToPortfolioRoute> get() = model.bottomSheetNavigation
override val addToPortfolioManager: AddToPortfolioManager? get() = model.addToPortfolioManager
override val addToPortfolioCallback: AddToPortfolioComponent.Callback get() = model.addToPortfolioCallback
override val addToPortfolioManager: AddToPortfolioManager get() = model.addToPortfolioManager
override val uiState: StateFlow<TokenListUM>
get() = model.state

View file

@ -41,8 +41,7 @@ import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.feature.swap.domain.GetAvailablePairsUseCase
import com.tangem.feature.swap.domain.models.domain.LeastTokenInfo
import com.tangem.feature.swap.domain.models.domain.SwapPairLeast
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioComponent
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioManager
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.swap.availablepairs.AvailableSwapPairsComponent
import com.tangem.features.onramp.swap.availablepairs.entity.transformers.SetErrorWarningTransformer
@ -62,13 +61,10 @@ import com.tangem.features.onramp.utils.ClearSearchBarTransformer
import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer
import com.tangem.features.onramp.utils.UpdateSearchBarCallbacksTransformer
import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer
import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.runSuspendCatching
import com.tangem.utils.coroutines.saveIn
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@ -93,7 +89,6 @@ internal class AvailableSwapPairsModel @Inject constructor(
private val addToPortfolioManagerFactory: AddToPortfolioManager.Factory,
private val excludedBlockchains: ExcludedBlockchains,
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
swapFeatureToggles: SwapFeatureToggles,
getWalletsUseCase: GetWalletsUseCase,
) : Model() {
@ -104,14 +99,12 @@ internal class AvailableSwapPairsModel @Inject constructor(
private val allUserWallets = getWalletsUseCase.invokeSync()
val bottomSheetNavigation: SlotNavigation<AddToPortfolioRoute> = SlotNavigation()
var addToPortfolioManager: AddToPortfolioManager? = null
val addToPortfolioCallback: AddToPortfolioComponent.Callback = object : AddToPortfolioComponent.Callback {
override fun onDismiss() = bottomSheetNavigation.dismiss()
override fun onSuccess(addedToken: CryptoCurrency) {
onTokenAddedToPortfolio(addedToken)
}
}
private val addToPortfolioJobHolder = JobHolder()
val addToPortfolioManager: AddToPortfolioManager = addToPortfolioManagerFactory
.create(
scope = modelScope,
analyticsParams = AddToPortfolioManager.AnalyticsParams(source = ScreensSources.Swap.value),
settings = AddToPortfolioManager.Settings.ChooseToken,
)
private val accountListFlow = getAccountListUseCaseFlow()
private val availablePairsByNetworkFlow = MutableStateFlow<Map<LeastTokenInfo, AvailablePairsState>>(emptyMap())
@ -155,10 +148,14 @@ internal class AvailableSwapPairsModel @Inject constructor(
subscribeOnSelectedStatusChange()
subscribeOnAvailablePairsUpdates()
if (swapFeatureToggles.isMarketListFeatureEnabled) {
subscribeOnMarketsUpdates()
subscribeOnVisibleMarketItems()
}
subscribeOnMarketsUpdates()
subscribeOnVisibleMarketItems()
addToPortfolioManager.onDismiss.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.launchIn(modelScope)
addToPortfolioManager.onSuccessAdded.receiveAsFlow()
.onEach { result -> onTokenAddedToPortfolio(result.addedCurrency.currency) }
.launchIn(modelScope)
}
private fun getAccountListUseCaseFlow(): SharedFlow<List<AccountStatus>> {
@ -251,7 +248,7 @@ internal class AvailableSwapPairsModel @Inject constructor(
.associate { accountStatus ->
val statuses = accountStatus.tokenList.flattenCurrencies()
.filterNot { status ->
status.currency.network.backendId == selectedStatus?.currency?.network?.backendId &&
status.currency.network.rawId == selectedStatus?.currency?.network?.rawId &&
status.currency.id.contractAddress == selectedStatus.currency.id.contractAddress
}
.filterByQuery(query = query)
@ -458,7 +455,7 @@ internal class AvailableSwapPairsModel @Inject constructor(
private fun CryptoCurrencyStatus.toLeastTokenInfo(): LeastTokenInfo {
return LeastTokenInfo(
contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress ?: "0",
network = currency.network.backendId,
network = currency.network.rawId,
)
}
@ -593,45 +590,35 @@ internal class AvailableSwapPairsModel @Inject constructor(
}
private fun addToPortfolioItem(item: MarketsListItemUM) {
modelScope.launch {
val tokenMarket = defaultMarketsListManager.getTokenMarketById(item.id)
?: searchMarketsListManager.getTokenMarketById(item.id)
?: return@launch
val tokenMarket = defaultMarketsListManager.getTokenMarketById(item.id)
?: searchMarketsListManager.getTokenMarketById(item.id)
?: return
val param = tokenMarket.toSerializableParam()
val hasOnlyHotWallets = allUserWallets.all { it is UserWallet.Hot }
val param = tokenMarket.toSerializableParam()
val hasOnlyHotWallets = allUserWallets.all { it is UserWallet.Hot }
val networks = tokenMarket.networks?.filter { network ->
BlockchainUtils.isSupportedNetworkId(
blockchainId = network.networkId,
coinId = tokenMarket.id.value,
contractAddress = network.contractAddress,
excludedBlockchains = excludedBlockchains,
hotExcludedBlockchains = hotWalletExcludedBlockchains,
hasOnlyHotWallets = hasOnlyHotWallets,
)
}?.map { network ->
TokenMarketInfo.Network(
networkId = network.networkId,
isExchangeable = false,
contractAddress = network.contractAddress,
decimalCount = network.decimalCount,
)
}.orEmpty()
val networks = tokenMarket.networks?.filter { network ->
BlockchainUtils.isSupportedNetworkId(
networkId = network.networkId,
coinId = tokenMarket.id.value,
contractAddress = network.contractAddress,
excludedBlockchains = excludedBlockchains,
hotExcludedBlockchains = hotWalletExcludedBlockchains,
hasOnlyHotWallets = hasOnlyHotWallets,
)
}?.map { network ->
TokenMarketInfo.Network(
networkId = network.networkId,
isExchangeable = false,
contractAddress = network.contractAddress,
decimalCount = network.decimalCount,
)
}.orEmpty()
addToPortfolioManager = addToPortfolioManagerFactory
.create(
scope = modelScope,
token = param,
analyticsParams = AddToPortfolioManager.AnalyticsParams(source = ScreensSources.Swap.value),
).apply {
setTokenNetworks(networks)
}
addToPortfolioManager.setTokenNetworks(networks)
addToPortfolioManager.setTokenParams(param)
addToPortfolioManager?.state
?.firstOrNull { it is AddToPortfolioManager.State.AvailableToAdd }
?.run { bottomSheetNavigation.activate(AddToPortfolioRoute) }
}.saveIn(addToPortfolioJobHolder)
bottomSheetNavigation.activate(AddToPortfolioRoute)
}
private fun subscribeOnVisibleMarketItems() {