diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt index 9780c3831c..1e759205fc 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt @@ -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?): 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, + ) + } + } + } } \ No newline at end of file diff --git a/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addtoportfolio/AddToPortfolioComponent.kt b/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addtoportfolio/AddToPortfolioComponent.kt index 7e3d52793e..2261f0335d 100644 --- a/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addtoportfolio/AddToPortfolioComponent.kt +++ b/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addtoportfolio/AddToPortfolioComponent.kt @@ -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 } \ No newline at end of file diff --git a/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addtoportfolio/AddToPortfolioManager.kt b/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addtoportfolio/AddToPortfolioManager.kt index 594e0e18ec..9ab9c5a86c 100644 --- a/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addtoportfolio/AddToPortfolioManager.kt +++ b/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addtoportfolio/AddToPortfolioManager.kt @@ -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 + val onSuccessAdded: Channel - // todo swap make updatable - val token: TokenMarketParams - val analyticsParams: AnalyticsParams? val portfolioFetcher: PortfolioFetcher - val state: StateFlow - val allAvailableNetworks: Flow> fun setTokenNetworks(networks: List) + 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, + 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 + val settings: AddToPortfolioManager.Settings + val analyticsParams: AnalyticsParams + + suspend fun token(): TokenMarketParams = paramsFlow.first().token + + fun onDismiss() + fun onSuccessAdded(result: AddToPortfolioManager.Result) } \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/AddTokenComponent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/AddTokenComponent.kt index 9175781e37..a893a9c1c4 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/AddTokenComponent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/AddTokenComponent.kt @@ -36,7 +36,7 @@ internal class AddTokenComponent @AssistedInject constructor( } data class Params( - val eventBuilder: PortfolioAnalyticsEvent.EventBuilder, + val eventBuilder: Flow, val selectedPortfolio: Flow, val selectedNetwork: Flow, val callbacks: Callbacks, diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/DefaultAddToPortfolioComponent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/DefaultAddToPortfolioComponent.kt index b072275b3b..6ce1279b9d 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/DefaultAddToPortfolioComponent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/DefaultAddToPortfolioComponent.kt @@ -71,7 +71,7 @@ internal class DefaultAddToPortfolioComponent @AssistedInject constructor( } override fun dismiss() { - params.callback.onDismiss() + model.addToPortfolioManager.onDismiss() } @Composable diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/DefaultAddToPortfolioPreselectedDataComponent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/DefaultAddToPortfolioPreselectedDataComponent.kt index f2b8949242..f298a48b32 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/DefaultAddToPortfolioPreselectedDataComponent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/DefaultAddToPortfolioPreselectedDataComponent.kt @@ -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, diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/TokenActionsComponent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/TokenActionsComponent.kt index 4f2af5aa01..c5e6430356 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/TokenActionsComponent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/TokenActionsComponent.kt @@ -63,7 +63,7 @@ internal class TokenActionsComponent @AssistedInject constructor( ) data class Params( - val eventBuilder: PortfolioAnalyticsEvent.EventBuilder, + val eventBuilder: Flow, val data: Flow, val callbacks: Callbacks, ) diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddToPortfolioModel.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddToPortfolioModel.kt index 4582cc2853..a0b119a4ab 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddToPortfolioModel.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddToPortfolioModel.kt @@ -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 = replayMutableSharedFlow() val tokenActionsData: MutableSharedFlow = 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 = replayMutableSharedFlow() val featureData: Flow = combineFeatureData() @@ -85,32 +83,27 @@ internal class AddToPortfolioModel @Inject constructor( @Suppress("LongMethod") private fun startAddToPortfolioFlow() { channelFlow { - 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 = featureData - .filterIsInstance() + .filterIsInstance() .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 } } diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddTokenModel.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddTokenModel.kt index 4bb4f90351..ad1eee5632 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddTokenModel.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddTokenModel.kt @@ -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() - private val analyticsEventBuilder = params.eventBuilder private val addTokenJob = JobHolder() val uiState: StateFlow @@ -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 { diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsModel.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsModel.kt index 2a9e691f46..575c386ebd 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsModel.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsModel.kt @@ -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 = SlotNavigation() val uiState: StateFlow = 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, diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsUiBuilder.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsUiBuilder.kt index 5b12b3efd4..2e6e031449 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsUiBuilder.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsUiBuilder.kt @@ -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() - 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), diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/DefaultAddToPortfolioManager.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/DefaultAddToPortfolioManager.kt index 0093289a88..a0aa017d17 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/DefaultAddToPortfolioManager.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/DefaultAddToPortfolioManager.kt @@ -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>( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST, - ) + override val onDismiss: Channel = Channel() + override val onSuccessAdded: Channel = Channel() - override val allAvailableNetworks: Flow> = _allAvailableNetworks.asSharedFlow() override val portfolioFetcher: PortfolioFetcher = portfolioFetcherFactory.create( mode = PortfolioFetcher.Mode.All(isOnlyMultiCurrency = true), scope = scope, ) - override val state: StateFlow = - 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 = + 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) { - _allAvailableNetworks.tryEmit(networks) + updateInternal(networks = networks) + } + + override fun setTokenParams(token: TokenMarketParams) { + updateInternal(token = token) + } + + private fun updateInternal(networks: List? = 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 = 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? = null, + val token: TokenMarketParams? = null, + ) } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/DefaultMarketsPortfolioComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/DefaultMarketsPortfolioComponent.kt index b10ad7940d..8e86530324 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/DefaultMarketsPortfolioComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/DefaultMarketsPortfolioComponent.kt @@ -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( diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioModel.kt index 93b2b5629a..b98f118a73 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioModel.kt @@ -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 = 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) { @@ -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 = { diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/DefaultSwapSelectTokensComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/DefaultSwapSelectTokensComponent.kt index f09d27300f..d18755aaf4 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/DefaultSwapSelectTokensComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/DefaultSwapSelectTokensComponent.kt @@ -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, ), ) } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/AvailableSwapPairsComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/AvailableSwapPairsComponent.kt index e8d885de0e..2292f1bdf8 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/AvailableSwapPairsComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/AvailableSwapPairsComponent.kt @@ -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 { val bottomSheetNavigation: SlotNavigation - val addToPortfolioManager: AddToPortfolioManager? - val addToPortfolioCallback: AddToPortfolioComponent.Callback + val addToPortfolioManager: AddToPortfolioManager /** Component factory */ interface Factory : ComponentFactory diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/DefaultAvailableSwapPairsComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/DefaultAvailableSwapPairsComponent.kt index 23816a7b3a..347d4ad933 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/DefaultAvailableSwapPairsComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/DefaultAvailableSwapPairsComponent.kt @@ -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 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 get() = model.state diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt index dc0066f473..3068b62dcf 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt @@ -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 = 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>(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> { @@ -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() { diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/DefaultChooseTokenComponent.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/DefaultChooseTokenComponent.kt index b92904c2d7..4b97a57ebe 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/DefaultChooseTokenComponent.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/DefaultChooseTokenComponent.kt @@ -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, ), ) } diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/converter/ChooseTokenListItemConverter.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/converter/ChooseTokenListItemConverter.kt index ee77c4eeec..02051e6d74 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/converter/ChooseTokenListItemConverter.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/converter/ChooseTokenListItemConverter.kt @@ -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( diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/ChooseTokenModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/ChooseTokenModel.kt index 12459d97e7..d0fd563a00 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/ChooseTokenModel.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/ChooseTokenModel.kt @@ -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> = MutableStateFlow(emptyMap()) private val onWalletSelected = Channel() - // 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 = combineUIOld() private val contentState: StateFlow = 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 = combine( flow = bridge.currenciesGroup, diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/MarketBlockDelegate.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/MarketBlockDelegate.kt index 2c66851ed2..36f5f5ebc2 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/MarketBlockDelegate.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/MarketBlockDelegate.kt @@ -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>(emptyList()) private val visibleDefaultMarketItemIds = MutableStateFlow>(emptyList()) val addToPortfolioSlot: SlotNavigation = SlotNavigation() - var addToPortfolioManager: AddToPortfolioManager? = null + val addToPortfolioManager: AddToPortfolioManager = addToPortfolioManagerFactory.create( + scope = modelScope, + settings = AddToPortfolioManager.Settings.ChooseToken, + analyticsParams = AddToPortfolioManager.AnalyticsParams(source = screensSourcesName), + ) val marketsStateFlow: Flow = 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