Updated on 2026-08-14
This commit is contained in:
parent
5f619a56fe
commit
9a0fbcbe1b
22 changed files with 324 additions and 254 deletions
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue