Updated on 2026-08-14
This commit is contained in:
parent
5f619a56fe
commit
9a0fbcbe1b
22 changed files with 324 additions and 254 deletions
|
|
@ -127,24 +127,6 @@ class AccountCryptoPortfolioItemStateConverter(
|
|||
isFlickering = this.source.isFlickering(),
|
||||
)
|
||||
|
||||
private fun createFiatAmountState(fiatBalance: TotalFiatBalance, appCurrency: AppCurrency): FiatAmountState {
|
||||
return when (fiatBalance) {
|
||||
TotalFiatBalance.Failed,
|
||||
TotalFiatBalance.Loading,
|
||||
-> FiatAmountState.Empty
|
||||
|
||||
is TotalFiatBalance.Loaded -> FiatAmountState.Content(
|
||||
text = fiatBalance.amount.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
},
|
||||
isFlickering = fiatBalance.source == StatusSource.CACHE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSubtitle2State(priceChangeLce: Lce<Unit, PriceChange>?): Subtitle2State? {
|
||||
return priceChangeLce?.fold(
|
||||
ifLoading = { priceChange -> priceChange?.toSubtitle2State() ?: Subtitle2State.Loading },
|
||||
|
|
@ -152,4 +134,24 @@ class AccountCryptoPortfolioItemStateConverter(
|
|||
ifContent = { priceChange -> priceChange.toSubtitle2State() },
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun createFiatAmountState(fiatBalance: TotalFiatBalance, appCurrency: AppCurrency): FiatAmountState {
|
||||
return when (fiatBalance) {
|
||||
TotalFiatBalance.Failed,
|
||||
TotalFiatBalance.Loading,
|
||||
-> FiatAmountState.Empty
|
||||
|
||||
is TotalFiatBalance.Loaded -> FiatAmountState.Content(
|
||||
text = fiatBalance.amount.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
},
|
||||
isFlickering = fiatBalance.source == StatusSource.CACHE,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,21 +2,12 @@ package com.tangem.features.commonfeatures.api.addtoportfolio
|
|||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
||||
interface AddToPortfolioComponent : ComposableBottomSheetComponent {
|
||||
|
||||
data class Params(
|
||||
val addToPortfolioManager: AddToPortfolioManager,
|
||||
val callback: Callback,
|
||||
val shouldSkipTokenActionsScreen: Boolean = false,
|
||||
)
|
||||
|
||||
interface Callback {
|
||||
fun onDismiss()
|
||||
// todo swap add new onSuccess with full data of added token
|
||||
fun onSuccess(addedToken: CryptoCurrency)
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, AddToPortfolioComponent>
|
||||
}
|
||||
|
|
@ -2,43 +2,90 @@ package com.tangem.features.commonfeatures.api.addtoportfolio
|
|||
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager.AnalyticsParams
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
interface AddToPortfolioManager {
|
||||
interface AddToPortfolioManager : AddToPortfolioManagerInternal {
|
||||
|
||||
val onDismiss: Channel<Unit>
|
||||
val onSuccessAdded: Channel<Result>
|
||||
|
||||
// todo swap make updatable
|
||||
val token: TokenMarketParams
|
||||
val analyticsParams: AnalyticsParams?
|
||||
val portfolioFetcher: PortfolioFetcher
|
||||
|
||||
val state: StateFlow<State>
|
||||
|
||||
val allAvailableNetworks: Flow<List<TokenMarketInfo.Network>>
|
||||
fun setTokenNetworks(networks: List<TokenMarketInfo.Network>)
|
||||
fun setTokenParams(token: TokenMarketParams)
|
||||
|
||||
sealed interface State {
|
||||
data object Init : State
|
||||
data class AvailableToAdd(
|
||||
data object Loading : State
|
||||
data class Ready(
|
||||
val availableToAddData: AvailableToAddData,
|
||||
) : State
|
||||
|
||||
data object NothingToAdd : State
|
||||
) : State {
|
||||
val isAvailableToAdd: Boolean get() = availableToAddData.isAvailableToAdd
|
||||
val isSinglePortfolio: Boolean get() = availableToAddData.isSinglePortfolio
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class AnalyticsParams(
|
||||
val source: String,
|
||||
val source: String?,
|
||||
)
|
||||
|
||||
interface Factory {
|
||||
fun create(
|
||||
scope: CoroutineScope,
|
||||
token: TokenMarketParams,
|
||||
analyticsParams: AnalyticsParams?,
|
||||
): AddToPortfolioManager
|
||||
fun create(scope: CoroutineScope, settings: Settings, analyticsParams: AnalyticsParams): AddToPortfolioManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Immutable settings
|
||||
*/
|
||||
data class Settings(
|
||||
val shouldSkipTokenActionsScreen: Boolean = false,
|
||||
) {
|
||||
companion object {
|
||||
val DefaultMarket = Settings(
|
||||
shouldSkipTokenActionsScreen = false,
|
||||
)
|
||||
val ChooseToken = Settings(
|
||||
shouldSkipTokenActionsScreen = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutable parameters
|
||||
* Updates may trigger reload [State]
|
||||
*/
|
||||
data class Params(
|
||||
val networks: List<TokenMarketInfo.Network>,
|
||||
val token: TokenMarketParams,
|
||||
)
|
||||
|
||||
data class Result(
|
||||
val wallet: UserWallet,
|
||||
val account: AccountStatus.CryptoPortfolio,
|
||||
val addedCurrency: CryptoCurrencyStatus,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* primary for internal impl usage, but you can also use it externally
|
||||
*/
|
||||
interface AddToPortfolioManagerInternal {
|
||||
val paramsFlow: SharedFlow<AddToPortfolioManager.Params>
|
||||
val settings: AddToPortfolioManager.Settings
|
||||
val analyticsParams: AnalyticsParams
|
||||
|
||||
suspend fun token(): TokenMarketParams = paramsFlow.first().token
|
||||
|
||||
fun onDismiss()
|
||||
fun onSuccessAdded(result: AddToPortfolioManager.Result)
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ internal class AddTokenComponent @AssistedInject constructor(
|
|||
}
|
||||
|
||||
data class Params(
|
||||
val eventBuilder: PortfolioAnalyticsEvent.EventBuilder,
|
||||
val eventBuilder: Flow<PortfolioAnalyticsEvent.EventBuilder>,
|
||||
val selectedPortfolio: Flow<SelectedPortfolio>,
|
||||
val selectedNetwork: Flow<SelectedNetwork>,
|
||||
val callbacks: Callbacks,
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ internal class DefaultAddToPortfolioComponent @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun dismiss() {
|
||||
params.callback.onDismiss()
|
||||
model.addToPortfolioManager.onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -10,12 +10,13 @@ import com.tangem.core.decompose.context.child
|
|||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.account.PortfolioSelectorComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioPreselectedDataModel
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioRoutes
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
internal class DefaultAddToPortfolioPreselectedDataComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
|
|
@ -37,7 +38,7 @@ internal class DefaultAddToPortfolioPreselectedDataComponent @AssistedInject con
|
|||
private val addTokenComponent: AddTokenComponent = addTokenComponentFactory.create(
|
||||
context = child("addTokenComponent"),
|
||||
params = AddTokenComponent.Params(
|
||||
eventBuilder = model.eventBuilder,
|
||||
eventBuilder = flowOf(model.eventBuilder),
|
||||
callbacks = model,
|
||||
selectedPortfolio = model.selectedPortfolio,
|
||||
selectedNetwork = model.selectedNetwork,
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ internal class TokenActionsComponent @AssistedInject constructor(
|
|||
)
|
||||
|
||||
data class Params(
|
||||
val eventBuilder: PortfolioAnalyticsEvent.EventBuilder,
|
||||
val eventBuilder: Flow<PortfolioAnalyticsEvent.EventBuilder>,
|
||||
val data: Flow<CryptoCurrencyData>,
|
||||
val callbacks: Callbacks,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.tangem.domain.markets.TokenMarketInfo
|
|||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
import com.tangem.features.account.PortfolioSelectorController
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.*
|
||||
import com.tangem.features.commonfeatures.impl.R
|
||||
|
|
@ -63,12 +64,9 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
val selectedPortfolio: MutableSharedFlow<SelectedPortfolio> = replayMutableSharedFlow()
|
||||
val tokenActionsData: MutableSharedFlow<CryptoCurrencyData> = replayMutableSharedFlow()
|
||||
|
||||
private val addToPortfolioManager = params.addToPortfolioManager
|
||||
val portfolioFetcher = addToPortfolioManager.portfolioFetcher
|
||||
val eventBuilder = PortfolioAnalyticsEvent.EventBuilder(
|
||||
tokenSymbol = addToPortfolioManager.token.symbol,
|
||||
source = addToPortfolioManager.analyticsParams?.source,
|
||||
)
|
||||
val addToPortfolioManager: AddToPortfolioManager = params.addToPortfolioManager
|
||||
val portfolioFetcher: PortfolioFetcher = addToPortfolioManager.portfolioFetcher
|
||||
val eventBuilder: MutableSharedFlow<PortfolioAnalyticsEvent.EventBuilder> = replayMutableSharedFlow()
|
||||
|
||||
val featureData: Flow<AddToPortfolioManager.State> = combineFeatureData()
|
||||
|
||||
|
|
@ -85,32 +83,27 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
@Suppress("LongMethod")
|
||||
private fun startAddToPortfolioFlow() {
|
||||
channelFlow<Unit> {
|
||||
fun finishFlow() {
|
||||
params.callback.onDismiss()
|
||||
channel.close()
|
||||
}
|
||||
|
||||
fun finishSuccessFlow(addedToken: CryptoCurrencyStatus) {
|
||||
params.callback.onSuccess(addedToken.currency)
|
||||
fun finishSuccessFlow(result: AddToPortfolioManager.Result) {
|
||||
addToPortfolioManager.onSuccessAdded(result)
|
||||
channel.close()
|
||||
}
|
||||
|
||||
val featureDataFlow: StateFlow<AvailableToAddData> = featureData
|
||||
.filterIsInstance<AddToPortfolioManager.State.AvailableToAdd>()
|
||||
.filterIsInstance<AddToPortfolioManager.State.Ready>()
|
||||
.map { it.availableToAddData }
|
||||
.distinctUntilChanged()
|
||||
.stateIn(this)
|
||||
val isAccountMode = portfolioSelectorController.isAccountModeSync()
|
||||
val tokenMarketParams = addToPortfolioManager.paramsFlow.first().token
|
||||
val eb = PortfolioAnalyticsEvent.EventBuilder(
|
||||
tokenSymbol = tokenMarketParams.symbol,
|
||||
source = addToPortfolioManager.analyticsParams.source,
|
||||
)
|
||||
eventBuilder.tryEmit(eb)
|
||||
|
||||
// use snapshot data, looks like we don’t need to remap at runtime
|
||||
val data = featureDataFlow.value
|
||||
|
||||
// you must control it via [AddToPortfolioManager.state]
|
||||
if (!data.isAvailableToAdd) {
|
||||
finishFlow()
|
||||
return@channelFlow
|
||||
}
|
||||
|
||||
// init data flows, emits on user/code selection, updates state holder
|
||||
val firstSelectedPortfolio = setupPortfolioFlow(data)
|
||||
.onEach { selectedPortfolio.emit(it) }
|
||||
|
|
@ -157,7 +150,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
// line of navigation to AddToken screen is finished; cancel the job, select a new root screen
|
||||
firstPartOfNavigation.cancel()
|
||||
|
||||
analyticsEventHandler.send(event = eventBuilder.popupToConfirm())
|
||||
analyticsEventHandler.send(event = eventBuilder.first().popupToConfirm())
|
||||
navigation.replaceAll(AddToPortfolioRoutes.AddToken)
|
||||
|
||||
var middleNavigationJob: Job? = null
|
||||
|
|
@ -185,34 +178,39 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
val addedToken = callbackDelegate.onTokenAdded.receiveAsFlow().first()
|
||||
middleNavigationJob?.cancel()
|
||||
val selectedPortfolio = selectedPortfolio.first()
|
||||
val result = AddToPortfolioManager.Result(
|
||||
wallet = selectedPortfolio.userWallet,
|
||||
account = selectedPortfolio.account.account,
|
||||
addedCurrency = addedToken,
|
||||
)
|
||||
|
||||
messageSender.send(ToastMessage(message = resourceReference(R.string.markets_token_added)))
|
||||
|
||||
if (params.shouldSkipTokenActionsScreen) {
|
||||
finishSuccessFlow(addedToken)
|
||||
if (addToPortfolioManager.settings.shouldSkipTokenActionsScreen) {
|
||||
finishSuccessFlow(result)
|
||||
} else {
|
||||
setupTokenActionsFlow(selectedPortfolio, addedToken)
|
||||
.onEach { cryptoCurrencyData ->
|
||||
tokenActionsData.emit(cryptoCurrencyData)
|
||||
navigation.replaceAll(AddToPortfolioRoutes.TokenActions)
|
||||
}
|
||||
.onEmpty { finishFlow() }
|
||||
.onEmpty { finishSuccessFlow(result) }
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
callbackDelegate.onLaterClick.receiveAsFlow().first()
|
||||
finishFlow()
|
||||
finishSuccessFlow(result)
|
||||
}
|
||||
.catch { throwable ->
|
||||
TangemLogger.e("Error", throwable)
|
||||
params.callback.onDismiss()
|
||||
addToPortfolioManager.onDismiss()
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun logAccountSelector(isAccountMode: Boolean) {
|
||||
private suspend fun logAccountSelector(isAccountMode: Boolean) {
|
||||
if (isAccountMode) {
|
||||
analyticsEventHandler.send(eventBuilder.popupToChooseAccount())
|
||||
analyticsEventHandler.send(eventBuilder.first().popupToChooseAccount())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +289,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
data.availableToAddWallets[selectedAccountId.userWalletId] ?: return@combine null
|
||||
val availableToAddAccount =
|
||||
availableToAddWallets.availableToAddAccounts[selectedAccountId] ?: return@combine null
|
||||
if (!isAccountMode) analyticsEventHandler.send(eventBuilder.addToPortfolioWalletChanged())
|
||||
if (!isAccountMode) analyticsEventHandler.send(eventBuilder.first().addToPortfolioWalletChanged())
|
||||
SelectedPortfolio(
|
||||
isAccountMode = isAccountMode,
|
||||
userWallet = availableToAddWallets.userWallet,
|
||||
|
|
@ -327,7 +325,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
val accountIndex = account.account.account.derivationIndex
|
||||
return getTokenMarketCryptoCurrency(
|
||||
userWalletId = userWallet.walletId,
|
||||
tokenMarketParams = addToPortfolioManager.token,
|
||||
tokenMarketParams = addToPortfolioManager.token(),
|
||||
network = network,
|
||||
accountIndex = accountIndex,
|
||||
)
|
||||
|
|
@ -339,7 +337,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
|
||||
private fun combineFeatureData() = addToPortfolioManager.state.onEach { state ->
|
||||
when (state) {
|
||||
is AddToPortfolioManager.State.AvailableToAdd ->
|
||||
is AddToPortfolioManager.State.Ready ->
|
||||
portfolioSelectorController.isEnabled.value = isEnabled@{ userWallet, accountStatus ->
|
||||
val availableWallet = state.availableToAddData.availableToAddWallets[userWallet.walletId]
|
||||
?: return@isEnabled false
|
||||
|
|
@ -348,8 +346,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
?.isAvailableToAdd == true
|
||||
return@isEnabled isAvailableAccount
|
||||
}
|
||||
AddToPortfolioManager.State.Init,
|
||||
AddToPortfolioManager.State.NothingToAdd,
|
||||
AddToPortfolioManager.State.Loading,
|
||||
-> Unit
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ import com.tangem.core.ui.message.ToastMessage
|
|||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.wallets.usecase.ColdWalletAndHasMissedDerivationsUseCase
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.AddTokenComponent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddTokenUiBuilder.Companion.toggleProgress
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.SelectedNetwork
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.SelectedPortfolio
|
||||
import com.tangem.features.commonfeatures.impl.R
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.AddTokenComponent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddTokenUiBuilder.Companion.toggleProgress
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
|
|
@ -40,7 +40,6 @@ internal class AddTokenModel @Inject constructor(
|
|||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<AddTokenComponent.Params>()
|
||||
private val analyticsEventBuilder = params.eventBuilder
|
||||
private val addTokenJob = JobHolder()
|
||||
|
||||
val uiState: StateFlow<AddTokenUM?>
|
||||
|
|
@ -86,10 +85,11 @@ internal class AddTokenModel @Inject constructor(
|
|||
uiState.value = um.toggleProgress(true)
|
||||
val blockchainNames = listOf(selectedNetwork.selectedNetwork)
|
||||
.mapNotNull { BlockchainUtils.getNetworkInfo(it.networkId)?.name }
|
||||
val analyticsEventBuilder = params.eventBuilder.first()
|
||||
analyticsEventHandler.send(analyticsEventBuilder.addToPortfolioContinue(blockchainNames))
|
||||
analyticsEventHandler.send(analyticsEventBuilder.addButtonClick())
|
||||
|
||||
manageCryptoCurrenciesUseCase(accountId = accountId, add = cryptoCurrency)
|
||||
manageCryptoCurrenciesUseCase.invokeAndAwait(accountId = accountId, add = listOf(cryptoCurrency))
|
||||
.onLeft { throwable ->
|
||||
processError(error = throwable)
|
||||
uiState.value = um.toggleProgress(false)
|
||||
|
|
@ -100,7 +100,8 @@ internal class AddTokenModel @Inject constructor(
|
|||
userWalletId = accountId.userWalletId,
|
||||
currencyId = cryptoCurrency.id,
|
||||
network = cryptoCurrency.network,
|
||||
).firstOrNull()
|
||||
)
|
||||
.firstOrNull()
|
||||
if (status == null) {
|
||||
processError(error = null)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import com.tangem.utils.Provider
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -52,18 +53,18 @@ internal class TokenActionsModel @Inject constructor(
|
|||
|
||||
val bottomSheetNavigation: SlotNavigation<TokenReceiveConfig> = SlotNavigation()
|
||||
val uiState: StateFlow<TokenActionsUM?> = params.data
|
||||
.mapLatest { uiBuilder.build(it, tokenActionsHandler) }
|
||||
.mapLatest { uiBuilder.build(it, tokenActionsHandler, analyticsEventBuilder.first()) }
|
||||
.stateIn(
|
||||
scope = modelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = null,
|
||||
)
|
||||
|
||||
private fun handledQuickAction(handledAction: TokenActionsHandler.HandledQuickAction) {
|
||||
val event = analyticsEventBuilder.getTokenActionClick(actionUM = handledAction.action)
|
||||
private fun handledQuickAction(handledAction: TokenActionsHandler.HandledQuickAction) = modelScope.launch {
|
||||
val event = analyticsEventBuilder.first().getTokenActionClick(actionUM = handledAction.action)
|
||||
analyticsEventHandler.send(event)
|
||||
val isReceive = handledAction.action == TokenActionsBSContentUM.Action.Receive
|
||||
if (!isReceive) return
|
||||
if (!isReceive) return@launch
|
||||
modelScope.launch {
|
||||
val tokenConfig = receiveAddressesFactory.create(
|
||||
status = handledAction.cryptoCurrencyData.status,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIco
|
|||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.TokenActionsComponent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.analytics.PortfolioAnalyticsEvent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.TokenActionsUM
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -20,7 +21,11 @@ internal class TokenActionsUiBuilder @Inject constructor(
|
|||
) {
|
||||
private val params = paramsContainer.require<TokenActionsComponent.Params>()
|
||||
|
||||
fun build(data: CryptoCurrencyData, tokenActionsHandler: TokenActionsHandler): TokenActionsUM {
|
||||
fun build(
|
||||
data: CryptoCurrencyData,
|
||||
tokenActionsHandler: TokenActionsHandler,
|
||||
eventBuilder: PortfolioAnalyticsEvent.EventBuilder,
|
||||
): TokenActionsUM {
|
||||
val status = data.status
|
||||
val tokenUM = TokenItemState.Content(
|
||||
id = status.currency.id.value,
|
||||
|
|
@ -35,7 +40,7 @@ internal class TokenActionsUiBuilder @Inject constructor(
|
|||
return TokenActionsUM(
|
||||
token = tokenUM,
|
||||
onLaterClick = {
|
||||
analyticsEventHandler.send(params.eventBuilder.getTokenLater())
|
||||
analyticsEventHandler.send(eventBuilder.getTokenLater())
|
||||
params.callbacks.onLaterClick()
|
||||
},
|
||||
quickActions = quickActions(data, tokenActionsHandler),
|
||||
|
|
|
|||
|
|
@ -3,69 +3,109 @@ package com.tangem.features.commonfeatures.impl.addtoportfolio.ui
|
|||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.converter.AvailableToAddDataConverter
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager.AnalyticsParams
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager.Settings
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.converter.AvailableToAddDataConverter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
internal class DefaultAddToPortfolioManager @AssistedInject constructor(
|
||||
private val availableToAddDataConverter: AvailableToAddDataConverter,
|
||||
@Assisted override val token: TokenMarketParams,
|
||||
@Assisted override val analyticsParams: AddToPortfolioManager.AnalyticsParams?,
|
||||
@Assisted override val settings: Settings,
|
||||
@Assisted override val analyticsParams: AnalyticsParams,
|
||||
@Assisted val scope: CoroutineScope,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
portfolioFetcherFactory: PortfolioFetcher.Factory,
|
||||
) : AddToPortfolioManager {
|
||||
|
||||
private val _allAvailableNetworks = MutableSharedFlow<List<TokenMarketInfo.Network>>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
override val onDismiss: Channel<Unit> = Channel()
|
||||
override val onSuccessAdded: Channel<AddToPortfolioManager.Result> = Channel()
|
||||
|
||||
override val allAvailableNetworks: Flow<List<TokenMarketInfo.Network>> = _allAvailableNetworks.asSharedFlow()
|
||||
override val portfolioFetcher: PortfolioFetcher = portfolioFetcherFactory.create(
|
||||
mode = PortfolioFetcher.Mode.All(isOnlyMultiCurrency = true),
|
||||
scope = scope,
|
||||
)
|
||||
|
||||
override val state: StateFlow<AddToPortfolioManager.State> =
|
||||
combine(
|
||||
flow = portfolioFetcher.data.map { it.balances }.distinctUntilChanged(),
|
||||
flow2 = allAvailableNetworks.map { it.toSet() }.distinctUntilChanged(),
|
||||
) { balances, availableNetworks ->
|
||||
val data = availableToAddDataConverter.convert(
|
||||
balances = balances,
|
||||
availableNetworks = availableNetworks,
|
||||
marketParams = token,
|
||||
private val internalParamsFlow = MutableStateFlow(ParamsInternal())
|
||||
|
||||
override val paramsFlow = internalParamsFlow
|
||||
.transform { internalParams ->
|
||||
val fullParams = AddToPortfolioManager.Params(
|
||||
networks = internalParams.networks ?: return@transform,
|
||||
token = internalParams.token ?: return@transform,
|
||||
)
|
||||
if (data.isAvailableToAdd) {
|
||||
AddToPortfolioManager.State.AvailableToAdd(data)
|
||||
} else {
|
||||
AddToPortfolioManager.State.NothingToAdd
|
||||
}
|
||||
emit(fullParams)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
|
||||
|
||||
override val state: MutableStateFlow<AddToPortfolioManager.State> =
|
||||
MutableStateFlow(AddToPortfolioManager.State.Loading)
|
||||
|
||||
init {
|
||||
buildFlow()
|
||||
.onEach { newState -> state.update { newState } }
|
||||
.flowOn(dispatchers.default)
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = AddToPortfolioManager.State.Init,
|
||||
)
|
||||
.launchIn(scope)
|
||||
}
|
||||
|
||||
override fun onDismiss() {
|
||||
onDismiss.trySend(Unit)
|
||||
}
|
||||
|
||||
override fun onSuccessAdded(result: AddToPortfolioManager.Result) {
|
||||
onSuccessAdded.trySend(result)
|
||||
}
|
||||
|
||||
override fun setTokenNetworks(networks: List<TokenMarketInfo.Network>) {
|
||||
_allAvailableNetworks.tryEmit(networks)
|
||||
updateInternal(networks = networks)
|
||||
}
|
||||
|
||||
override fun setTokenParams(token: TokenMarketParams) {
|
||||
updateInternal(token = token)
|
||||
}
|
||||
|
||||
private fun updateInternal(networks: List<TokenMarketInfo.Network>? = null, token: TokenMarketParams? = null) {
|
||||
internalParamsFlow.update { prev ->
|
||||
val newParams = ParamsInternal(
|
||||
networks = networks ?: prev.networks,
|
||||
token = token ?: prev.token,
|
||||
)
|
||||
val shouldReload = prev.networks != newParams.networks || prev.token != newParams.token
|
||||
if (shouldReload) state.update { AddToPortfolioManager.State.Loading }
|
||||
return@update newParams
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildFlow(): Flow<AddToPortfolioManager.State> = combine(
|
||||
flow = portfolioFetcher.data.map { it.balances }.distinctUntilChanged(),
|
||||
flow2 = paramsFlow,
|
||||
) { balances, (availableNetworks, token) ->
|
||||
val data = availableToAddDataConverter.convert(
|
||||
balances = balances,
|
||||
availableNetworks = availableNetworks.toSet(),
|
||||
marketParams = token,
|
||||
)
|
||||
AddToPortfolioManager.State.Ready(data)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : AddToPortfolioManager.Factory {
|
||||
override fun create(
|
||||
scope: CoroutineScope,
|
||||
token: TokenMarketParams,
|
||||
analyticsParams: AddToPortfolioManager.AnalyticsParams?,
|
||||
settings: Settings,
|
||||
analyticsParams: AnalyticsParams,
|
||||
): DefaultAddToPortfolioManager
|
||||
}
|
||||
|
||||
private data class ParamsInternal(
|
||||
val networks: List<TokenMarketInfo.Network>? = null,
|
||||
val token: TokenMarketParams? = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -66,7 +66,6 @@ internal class DefaultMarketsPortfolioComponent @AssistedInject constructor(
|
|||
context = childByContext(componentContext),
|
||||
params = AddToPortfolioComponent.Params(
|
||||
addToPortfolioManager = model.addToPortfolioManager,
|
||||
callback = model.addToPortfolioCallback,
|
||||
),
|
||||
)
|
||||
is MarketsPortfolioRoute.TokenReceive -> tokenReceiveComponentFactory.create(
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@ import com.tangem.core.decompose.model.ParamsContainer
|
|||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
||||
import com.tangem.features.feed.components.market.details.portfolio.api.MarketsPortfolioComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.analytics.PortfolioAnalyticsEvent
|
||||
|
|
@ -58,16 +56,18 @@ internal class MarketsPortfolioModel @Inject constructor(
|
|||
private val marketsPortfolioDelegate: MarketsPortfolioDelegate = createMarketsPortfolioDelegate()
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<MarketsPortfolioRoute> = SlotNavigation()
|
||||
val addToPortfolioCallback = object : AddToPortfolioComponent.Callback {
|
||||
override fun onDismiss() = bottomSheetNavigation.dismiss()
|
||||
override fun onSuccess(addedToken: CryptoCurrency) = bottomSheetNavigation.dismiss()
|
||||
}
|
||||
|
||||
init {
|
||||
marketsPortfolioDelegate.combineData()
|
||||
.onEach { state.value = it }
|
||||
.flowOn(dispatchers.default)
|
||||
.launchIn(modelScope)
|
||||
addToPortfolioManager.onDismiss.receiveAsFlow()
|
||||
.onEach { bottomSheetNavigation.dismiss() }
|
||||
.launchIn(modelScope)
|
||||
addToPortfolioManager.onSuccessAdded.receiveAsFlow()
|
||||
.onEach { bottomSheetNavigation.dismiss() }
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
fun setTokenNetworks(networks: List<TokenMarketInfo.Network>) {
|
||||
|
|
@ -83,9 +83,9 @@ internal class MarketsPortfolioModel @Inject constructor(
|
|||
private fun createAddToPortfolioManager(): AddToPortfolioManager {
|
||||
return addToPortfolioManagerFactory.create(
|
||||
scope = modelScope,
|
||||
token = params.token,
|
||||
analyticsParams = params.analyticsParams?.source?.let { AddToPortfolioManager.AnalyticsParams(it) },
|
||||
)
|
||||
settings = AddToPortfolioManager.Settings.DefaultMarket,
|
||||
analyticsParams = AddToPortfolioManager.AnalyticsParams(params.analyticsParams?.source),
|
||||
).also { addToPortfolioManager -> addToPortfolioManager.setTokenParams(params.token) }
|
||||
}
|
||||
|
||||
private fun createMarketsPortfolioDelegate(): MarketsPortfolioDelegate {
|
||||
|
|
@ -95,11 +95,14 @@ internal class MarketsPortfolioModel @Inject constructor(
|
|||
tokenActionsHandler = tokenActionsHandler,
|
||||
buttonState = addToPortfolioManager.state.map { state ->
|
||||
when (state) {
|
||||
is AddToPortfolioManager.State.AvailableToAdd -> {
|
||||
MyPortfolioUM.Tokens.AddButtonState.Available
|
||||
is AddToPortfolioManager.State.Ready -> {
|
||||
if (state.isAvailableToAdd) {
|
||||
MyPortfolioUM.Tokens.AddButtonState.Available
|
||||
} else {
|
||||
MyPortfolioUM.Tokens.AddButtonState.Unavailable
|
||||
}
|
||||
}
|
||||
AddToPortfolioManager.State.Init -> MyPortfolioUM.Tokens.AddButtonState.Loading
|
||||
AddToPortfolioManager.State.NothingToAdd -> MyPortfolioUM.Tokens.AddButtonState.Unavailable
|
||||
AddToPortfolioManager.State.Loading -> MyPortfolioUM.Tokens.AddButtonState.Loading
|
||||
}
|
||||
},
|
||||
onAddClick = {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ 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.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
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
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ 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.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ 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.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.swap.availablepairs.AvailableSwapPairsComponent
|
||||
|
|
@ -66,9 +65,7 @@ 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
|
||||
|
|
@ -104,14 +101,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())
|
||||
|
|
@ -159,6 +154,12 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
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>> {
|
||||
|
|
@ -593,45 +594,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(
|
||||
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()
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -54,9 +54,7 @@ internal class DefaultChooseTokenComponent @AssistedInject constructor(
|
|||
return addToPortfolioComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = AddToPortfolioComponent.Params(
|
||||
addToPortfolioManager = model.addToPortfolioManager!!,
|
||||
callback = model.addToPortfolioCallback,
|
||||
shouldSkipTokenActionsScreen = true,
|
||||
addToPortfolioManager = model.addToPortfolioManager,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,10 +80,13 @@ internal class ChooseTokenListItemConverter(
|
|||
clickIntents.onAccountExpandClick(clickedAccount)
|
||||
}
|
||||
}
|
||||
val fiatAmountStateProvider: ((TotalFiatBalance) -> FiatAmountState?) = if (isSearchingState) {
|
||||
{ _ -> FiatAmountState.Empty }
|
||||
} else {
|
||||
{ _ -> FiatAmountState.Icon(R.drawable.ic_chewron_down_20, IconTint.Informative) }
|
||||
val fiatAmountStateProvider: ((TotalFiatBalance) -> FiatAmountState?) = { totalBalance ->
|
||||
when {
|
||||
isSearchingState -> FiatAmountState.Empty
|
||||
!isExpanded -> FiatAmountState.Icon(R.drawable.ic_chewron_down_20, IconTint.Informative)
|
||||
else -> AccountCryptoPortfolioItemStateConverter
|
||||
.createFiatAmountState(totalBalance, appCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
val converter = AccountCryptoPortfolioItemStateConverter(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
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.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -23,7 +22,6 @@ import com.tangem.feature.swap.converters.TokensDataConverter
|
|||
import com.tangem.feature.swap.models.SwapSelectTokenStateHolder
|
||||
import com.tangem.feature.swap.models.market.state.SwapMarketState
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
|
@ -73,17 +71,6 @@ internal class ChooseTokenModel @Inject constructor(
|
|||
private val expandedAccountsFlow: MutableStateFlow<Map<AccountId, Boolean>> = MutableStateFlow(emptyMap())
|
||||
private val onWalletSelected = Channel<UserWalletId>()
|
||||
|
||||
// todo swap call new api result
|
||||
val addToPortfolioCallback = object : AddToPortfolioComponent.Callback {
|
||||
override fun onDismiss() = marketBlockDelegate.addToPortfolioSlot.dismiss()
|
||||
|
||||
override fun onSuccess(addedToken: CryptoCurrency) {
|
||||
val newToken = addedToken to ChooseTokenAnalyticsPayload.IsSearched(isSearchingState)
|
||||
bridge.onNewTokenAdded(newToken)
|
||||
marketBlockDelegate.addToPortfolioSlot.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
val stateOld: StateFlow<SwapSelectTokenStateHolder?> = combineUIOld()
|
||||
|
||||
private val contentState: StateFlow<ChooseTokenUM?> = combineUI()
|
||||
|
|
@ -103,6 +90,27 @@ internal class ChooseTokenModel @Inject constructor(
|
|||
initialValue = ChooseTokenFullUM(initialState.value, contentState.value),
|
||||
)
|
||||
|
||||
init {
|
||||
addToPortfolioManager.onDismiss.receiveAsFlow()
|
||||
.onEach { marketBlockDelegate.addToPortfolioSlot.dismiss() }
|
||||
.launchIn(modelScope)
|
||||
addToPortfolioManager.onSuccessAdded.receiveAsFlow()
|
||||
.onEach { addedResult ->
|
||||
val isSearched = ChooseTokenAnalyticsPayload.IsSearched(isSearchingState)
|
||||
val newToken = addedResult.addedCurrency.currency to isSearched
|
||||
bridge.onNewTokenAdded(newToken)
|
||||
val chooseTokenResult = ChooseTokenResult(
|
||||
currency = addedResult.addedCurrency,
|
||||
account = addedResult.account,
|
||||
wallet = addedResult.wallet,
|
||||
analyticsPayload = setOf(isSearched),
|
||||
)
|
||||
bridge.onCurrencyChosen(chooseTokenResult)
|
||||
marketBlockDelegate.addToPortfolioSlot.dismiss()
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private fun combineUIOld(): StateFlow<SwapSelectTokenStateHolder?> = combine(
|
||||
flow = bridge.currenciesGroup,
|
||||
|
|
|
|||
|
|
@ -20,14 +20,11 @@ import com.tangem.feature.swap.models.market.state.SwapMarketState
|
|||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class MarketBlockDelegate @AssistedInject constructor(
|
||||
|
|
@ -40,12 +37,15 @@ internal class MarketBlockDelegate @AssistedInject constructor(
|
|||
@Assisted private val screensSourcesName: String,
|
||||
) {
|
||||
|
||||
private val addToPortfolioJobHolder = JobHolder()
|
||||
private val visibleMarketItemIds = MutableStateFlow<List<CryptoCurrency.RawID>>(emptyList())
|
||||
private val visibleDefaultMarketItemIds = MutableStateFlow<List<CryptoCurrency.RawID>>(emptyList())
|
||||
|
||||
val addToPortfolioSlot: SlotNavigation<AddToPortfolioRoute> = SlotNavigation()
|
||||
var addToPortfolioManager: AddToPortfolioManager? = null
|
||||
val addToPortfolioManager: AddToPortfolioManager = addToPortfolioManagerFactory.create(
|
||||
scope = modelScope,
|
||||
settings = AddToPortfolioManager.Settings.ChooseToken,
|
||||
analyticsParams = AddToPortfolioManager.AnalyticsParams(source = screensSourcesName),
|
||||
)
|
||||
|
||||
val marketsStateFlow: Flow<SwapMarketState> = searchQueryState
|
||||
// Switch between default and search market flows
|
||||
|
|
@ -177,45 +177,34 @@ internal class MarketBlockDelegate @AssistedInject 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 = getUserWalletsUseCase.invokeSync().all { it is UserWallet.Hot }
|
||||
val param = tokenMarket.toSerializableParam()
|
||||
val hasOnlyHotWallets = getUserWalletsUseCase.invokeSync().all { it is UserWallet.Hot }
|
||||
|
||||
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()
|
||||
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 = screensSourcesName),
|
||||
).apply {
|
||||
setTokenNetworks(networks)
|
||||
}
|
||||
addToPortfolioManager.setTokenNetworks(networks)
|
||||
addToPortfolioManager.setTokenParams(param)
|
||||
|
||||
addToPortfolioManager?.state
|
||||
?.firstOrNull { it is AddToPortfolioManager.State.AvailableToAdd }
|
||||
?.run { addToPortfolioSlot.activate(AddToPortfolioRoute) }
|
||||
}.saveIn(addToPortfolioJobHolder)
|
||||
addToPortfolioSlot.activate(AddToPortfolioRoute)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue