diff --git a/features/nft/api/build.gradle.kts b/features/nft/api/build.gradle.kts index d9217b0692..23f846a5b2 100644 --- a/features/nft/api/build.gradle.kts +++ b/features/nft/api/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation(projects.domain.models) implementation(projects.domain.nft.models) implementation(projects.domain.wallets.models) + implementation(projects.domain.account) /* Project - Core */ implementation(projects.core.decompose) diff --git a/features/send-v2/impl/build.gradle.kts b/features/send-v2/impl/build.gradle.kts index 9d3830f696..a4297a69f1 100644 --- a/features/send-v2/impl/build.gradle.kts +++ b/features/send-v2/impl/build.gradle.kts @@ -68,6 +68,8 @@ dependencies { implementation(projects.domain.nft) implementation(projects.domain.notifications) implementation(projects.domain.swap.models) + implementation(projects.domain.account) + implementation(projects.domain.account.status) /** Compose libraries */ diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt index f30c3fa513..858747f091 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt @@ -175,6 +175,8 @@ internal class DefaultSendComponent @AssistedInject constructor( userWalletId = params.userWalletId, cryptoCurrency = params.currency, cryptoCurrencyStatusFlow = model.cryptoCurrencyStatusFlow, + accountFlow = model.accountFlow, + isAccountModeFlow = model.isAccountModeFlow, callback = model, predefinedValues = model.predefinedValues, analyticsSendSource = model.analyticsSendSource, @@ -201,6 +203,8 @@ internal class DefaultSendComponent @AssistedInject constructor( feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, cryptoCurrencyStatusFlow = model.cryptoCurrencyStatusFlow, feeCryptoCurrencyStatusFlow = model.feeCryptoCurrencyStatusFlow, + accountFlow = model.accountFlow, + isAccountModeFlow = model.isAccountModeFlow, appCurrency = model.appCurrency, callback = model, predefinedValues = model.predefinedValues, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt index 154ab7e564..02c4c88993 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt @@ -11,6 +11,7 @@ import com.tangem.core.decompose.context.child import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.transaction.error.GetFeeError @@ -73,6 +74,8 @@ internal class SendConfirmComponent( cryptoCurrencyStatusFlow = params.cryptoCurrencyStatusFlow, isBalanceHidingFlow = params.isBalanceHidingFlow, analyticsSendSource = params.analyticsSendSource, + accountFlow = params.accountFlow, + isAccountModeFlow = params.isAccountModeFlow, ), onResult = model::onAmountResult, onClick = model::showEditAmount, @@ -152,6 +155,8 @@ internal class SendConfirmComponent( val feeCryptoCurrencyStatus: CryptoCurrencyStatus, val cryptoCurrencyStatusFlow: StateFlow, val feeCryptoCurrencyStatusFlow: StateFlow, + val accountFlow: StateFlow, + val isAccountModeFlow: StateFlow, val appCurrency: AppCurrency, val callback: ModelCallback, val currentRoute: Flow, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index 4b8551fd88..38c5c33abd 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -14,6 +14,9 @@ import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseBigDecimalOrNull +import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles +import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase +import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase @@ -23,6 +26,7 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.isMultiCurrency @@ -91,8 +95,11 @@ internal class SendModel @Inject constructor( private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val createTransferTransactionUseCase: CreateTransferTransactionUseCase, private val getFeeUseCase: GetFeeUseCase, + private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase, + private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase, private val sendAmountUpdateTrigger: SendAmountUpdateTrigger, private val analyticsEventHandler: AnalyticsEventHandler, + private val accountsFeatureToggles: AccountsFeatureToggles, ) : Model(), SendComponentCallback { private val params: SendComponent.Params = paramsContainer.require() @@ -115,21 +122,26 @@ internal class SendModel @Inject constructor( val currentRoute = MutableStateFlow(initialRoute) - private val _cryptoCurrencyStatusFlow = MutableStateFlow( - CryptoCurrencyStatus( - params.currency, - value = CryptoCurrencyStatus.Loading, - ), - ) - val cryptoCurrencyStatusFlow = _cryptoCurrencyStatusFlow.asStateFlow() + val cryptoCurrencyStatusFlow: StateFlow + field = MutableStateFlow( + CryptoCurrencyStatus( + params.currency, + value = CryptoCurrencyStatus.Loading, + ), + ) - private val _feeCryptoCurrencyStatusFlow = MutableStateFlow( - CryptoCurrencyStatus( - params.currency, - value = CryptoCurrencyStatus.Loading, - ), - ) - val feeCryptoCurrencyStatusFlow = _feeCryptoCurrencyStatusFlow.asStateFlow() + val feeCryptoCurrencyStatusFlow: StateFlow + field = MutableStateFlow( + CryptoCurrencyStatus( + params.currency, + value = CryptoCurrencyStatus.Loading, + ), + ) + + val accountFlow: StateFlow + field = MutableStateFlow(null) + val isAccountModeFlow: StateFlow + field = MutableStateFlow(false) var userWallet: UserWallet by Delegates.notNull() var appCurrency: AppCurrency = AppCurrency.Default @@ -317,13 +329,34 @@ internal class SendModel @Inject constructor( ifRight = { wallet -> userWallet = wallet - val isSingleWalletWithToken = wallet is UserWallet.Cold && - wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() - val isMultiCurrency = wallet.isMultiCurrency - getCurrenciesStatusUpdates( - isSingleWalletWithToken = isSingleWalletWithToken, - isMultiCurrency = isMultiCurrency, - ) + if (accountsFeatureToggles.isFeatureEnabled) { + getAccountCurrencyStatusUseCase( + userWalletId = params.userWalletId, + currency = cryptoCurrency, + ).onEach { (account, cryptoCurrencyStatus) -> + cryptoCurrencyStatusFlow.value = cryptoCurrencyStatus + feeCryptoCurrencyStatusFlow.value = getFeePaidCryptoCurrencyStatusSyncUseCase( + userWalletId = params.userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + ).getOrNull() ?: cryptoCurrencyStatus + + isAccountModeFlow.value = isAccountsModeEnabledUseCase.invokeSync() + accountFlow.value = account + + if (params.amount != null) { + router.replaceAll(Confirm) + } + }.flowOn(dispatchers.default) + .launchIn(modelScope) + } else { + val isSingleWalletWithToken = wallet is UserWallet.Cold && + wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() + val isMultiCurrency = wallet.isMultiCurrency + getCurrenciesStatusUpdates( + isSingleWalletWithToken = isSingleWalletWithToken, + isMultiCurrency = isMultiCurrency, + ) + } }, ifLeft = { Timber.w(it.toString()) @@ -352,10 +385,12 @@ internal class SendModel @Inject constructor( ).onEach { maybeCryptoCurrency -> maybeCryptoCurrency.fold( ifRight = { cryptoCurrencyStatus -> - onDataLoaded( - currencyStatus = cryptoCurrencyStatus, - feeCurrencyStatus = getFeeCurrencyStatus(cryptoCurrencyStatus, isMultiCurrency), - ) + cryptoCurrencyStatusFlow.value = cryptoCurrencyStatus + feeCryptoCurrencyStatusFlow.value = getFeeCurrencyStatus(cryptoCurrencyStatus, isMultiCurrency) + + if (params.amount != null) { + router.replaceAll(CommonSendRoute.Confirm) + } }, ifLeft = { sendConfirmAlertFactory.getGenericErrorState( @@ -366,7 +401,8 @@ internal class SendModel @Inject constructor( ) }, ) - }.launchIn(modelScope) + }.flowOn(dispatchers.default) + .launchIn(modelScope) } private fun getCurrencyStatus( @@ -402,15 +438,6 @@ internal class SendModel @Inject constructor( } } - private fun onDataLoaded(currencyStatus: CryptoCurrencyStatus, feeCurrencyStatus: CryptoCurrencyStatus) { - _cryptoCurrencyStatusFlow.value = currencyStatus - _feeCryptoCurrencyStatusFlow.value = feeCurrencyStatus - - if (params.amount != null) { - router.replaceAll(CommonSendRoute.Confirm) - } - } - private fun subscribeOnQRScannerResult() { listenToQrScanningUseCase(SourceType.SEND) .getOrElse { emptyFlow() } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt index 277b66f0a2..5128c82ff5 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt @@ -11,6 +11,9 @@ import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router import com.tangem.datasource.local.nft.converter.NFTSdkAssetConverter +import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles +import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase +import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.common.util.cardTypesResolver @@ -19,6 +22,7 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet @@ -71,6 +75,9 @@ internal class NFTSendModel @Inject constructor( private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, private val alertFactory: SendConfirmAlertFactory, private val nftSendSuccessTrigger: NFTSendSuccessTrigger, + private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase, + private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase, + private val accountsFeatureToggles: AccountsFeatureToggles, ) : Model(), SendNFTComponentCallback, NFTSendSuccessComponent.ModelCallback { val params: NFTSendComponent.Params = paramsContainer.require() @@ -94,6 +101,9 @@ internal class NFTSendModel @Inject constructor( var feeCryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull() var appCurrency: AppCurrency = AppCurrency.Default + var account: Account.CryptoPortfolio? = null + var isAccountsMode: Boolean = false + init { subscribeOnCurrencyStatusUpdates() initAppCurrency() @@ -176,10 +186,31 @@ internal class NFTSendModel @Inject constructor( ?.firstOrNull { it is CryptoCurrency.Coin && it.network == nftAsset.network } ?: return@launch - getCurrenciesStatusUpdates( - isSingleWalletWithToken = wallet is UserWallet.Cold && - wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(), - ) + if (accountsFeatureToggles.isFeatureEnabled) { + getAccountCurrencyStatusUseCase( + userWalletId, + cryptoCurrency, + ).onEach { (maybeAccount, cryptoStatus) -> + account = maybeAccount + isAccountsMode = isAccountsModeEnabledUseCase.invokeSync() + + cryptoCurrencyStatus = cryptoStatus + feeCryptoCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase( + userWalletId = userWalletId, + cryptoCurrencyStatus = cryptoStatus, + ).getOrNull() ?: cryptoStatus + + if (uiState.value.destinationUM is DestinationUM.Empty) { + router.replaceAll(Destination(isEditMode = false)) + } + }.flowOn(dispatchers.default) + .launchIn(modelScope) + } else { + getCurrenciesStatusUpdates( + isSingleWalletWithToken = wallet is UserWallet.Cold && + wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(), + ) + } }, ifLeft = { alertFactory.getGenericErrorState(::onFailedTxEmailClick) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt index 408453631d..d776414db6 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt @@ -2,6 +2,7 @@ package com.tangem.features.send.v2.subcomponents.amount import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet @@ -23,6 +24,8 @@ internal sealed class SendAmountComponentParams { abstract val cryptoCurrency: CryptoCurrency abstract val cryptoCurrencyStatusFlow: StateFlow abstract val isBalanceHidingFlow: StateFlow + abstract val accountFlow: StateFlow + abstract val isAccountModeFlow: StateFlow data class AmountParams( override val state: AmountState, @@ -34,6 +37,8 @@ internal sealed class SendAmountComponentParams { override val cryptoCurrencyStatusFlow: StateFlow, override val isBalanceHidingFlow: StateFlow, override val analyticsSendSource: CommonSendAnalyticEvents.CommonSendSource, + override val accountFlow: StateFlow, + override val isAccountModeFlow: StateFlow, val callback: ModelCallback, val currentRoute: StateFlow, ) : SendAmountComponentParams() @@ -48,6 +53,8 @@ internal sealed class SendAmountComponentParams { override val cryptoCurrencyStatusFlow: StateFlow, override val isBalanceHidingFlow: StateFlow, override val analyticsSendSource: CommonSendAnalyticEvents.CommonSendSource, + override val accountFlow: StateFlow, + override val isAccountModeFlow: StateFlow, val userWallet: UserWallet, val blockClickEnableFlow: StateFlow, ) : SendAmountComponentParams() diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt index aa40a2693e..621736fdea 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt @@ -21,6 +21,7 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.exchange.RampStateManager +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.isMultiCurrency @@ -118,42 +119,7 @@ internal class SendAmountModel @Inject constructor( private fun subscribeOnBalanceHiddenUpdates() { params.isBalanceHidingFlow.onEach { isBalanceHidden -> - _uiState.update( - AmountBoundaryUpdateTransformer( - cryptoCurrencyStatus = cryptoCurrencyStatus, - maxEnterAmount = maxAmountBoundary, - appCurrency = appCurrency, - isBalanceHidden = params.isBalanceHidingFlow.value, - ), - ) - }.launchIn(modelScope) - } - - private fun subscribeOnCryptoCurrencyStatusFlow() { - params.cryptoCurrencyStatusFlow - .onEach { newCryptoCurrencyStatus -> - cryptoCurrencyStatus = newCryptoCurrencyStatus - maxAmountBoundary = MaxEnterAmountConverter().convert(cryptoCurrencyStatus) - initMinBoundary() - } - .launchIn(modelScope) - } - - private fun initMinBoundary() { - modelScope.launch { - minAmountBoundary = getMinimumTransactionAmountSyncUseCase( - userWalletId = params.userWalletId, - cryptoCurrencyStatus = cryptoCurrencyStatus, - ).getOrNull()?.let { - EnterAmountBoundary( - amount = it, - fiatRate = cryptoCurrencyStatus.value.fiatRate.orZero(), - ) - } - - appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default } - - if (uiState.value is AmountState.Data) { + if (cryptoCurrencyStatus.value != CryptoCurrencyStatus.Loading) { _uiState.update( AmountBoundaryUpdateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, @@ -162,13 +128,61 @@ internal class SendAmountModel @Inject constructor( isBalanceHidden = params.isBalanceHidingFlow.value, ), ) - } else { - initialState() } + }.launchIn(modelScope) + } + + private fun subscribeOnCryptoCurrencyStatusFlow() { + combine( + flow = params.cryptoCurrencyStatusFlow.distinctUntilChanged { old, new -> + old.value.amount == new.value.amount + }, // Check only balance changes, + flow2 = params.accountFlow, + flow3 = params.isAccountModeFlow, + ) { newCryptoCurrencyStatus, account, isAccountsMode -> + cryptoCurrencyStatus = newCryptoCurrencyStatus + maxAmountBoundary = MaxEnterAmountConverter().convert(cryptoCurrencyStatus) + initMinBoundary(cryptoCurrencyStatus, account, isAccountsMode) + }.flowOn(dispatchers.default) + .launchIn(modelScope) + } + + private suspend fun initMinBoundary( + cryptoCurrencyStatus: CryptoCurrencyStatus, + account: Account.CryptoPortfolio?, + isAccountsMode: Boolean, + ) { + minAmountBoundary = getMinimumTransactionAmountSyncUseCase( + userWalletId = params.userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + ).getOrNull()?.let { + EnterAmountBoundary( + amount = it, + fiatRate = cryptoCurrencyStatus.value.fiatRate.orZero(), + ) + } + + appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default } + + if (uiState.value is AmountState.Data) { + _uiState.update( + AmountBoundaryUpdateTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxEnterAmount = maxAmountBoundary, + appCurrency = appCurrency, + isBalanceHidden = params.isBalanceHidingFlow.value, + ), + ) + } else { + initialState(cryptoCurrencyStatus, account, isAccountsMode) } } - private fun initialState() { + private fun initialState( + cryptoCurrencyStatus: CryptoCurrencyStatus, + @Suppress("UnusedParameter") account: Account.CryptoPortfolio?, + @Suppress("UnusedParameter") isAccountsMode: Boolean, + ) { if (uiState.value is AmountState.Empty && userWallet != null) { val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1 _uiState.update { diff --git a/features/swap-v2/impl/build.gradle.kts b/features/swap-v2/impl/build.gradle.kts index fed1e814da..b64e7ab8d8 100644 --- a/features/swap-v2/impl/build.gradle.kts +++ b/features/swap-v2/impl/build.gradle.kts @@ -68,6 +68,8 @@ dependencies { implementation(projects.domain.notifications) implementation(projects.domain.feedback.models) implementation(projects.domain.feedback) + implementation(projects.domain.account) + implementation(projects.domain.account.status) /** Compose */ implementation(deps.compose.foundation) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt index ee40d193e5..a2f39e1550 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt @@ -2,6 +2,7 @@ package com.tangem.features.swap.v2.impl.amount import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.express.models.ExpressProviderType +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet @@ -23,6 +24,8 @@ internal sealed class SwapAmountComponentParams { abstract val primaryCryptoCurrencyStatusFlow: StateFlow abstract val secondaryCryptoCurrency: CryptoCurrency? abstract val filterProviderTypes: List + abstract val accountFlow: StateFlow + abstract val isAccountModeFlow: StateFlow data class AmountParams( override val amountUM: SwapAmountUM, @@ -34,6 +37,8 @@ internal sealed class SwapAmountComponentParams { override val secondaryCryptoCurrency: CryptoCurrency?, override val filterProviderTypes: List = emptyList(), override val analyticsSendSource: CommonSendAnalyticEvents.CommonSendSource, + override val accountFlow: StateFlow, + override val isAccountModeFlow: StateFlow, val title: TextReference, val callback: SwapAmountComponent.ModelCallback, val currentRoute: Flow, @@ -49,6 +54,8 @@ internal sealed class SwapAmountComponentParams { override val secondaryCryptoCurrency: CryptoCurrency?, override val filterProviderTypes: List = emptyList(), override val analyticsSendSource: CommonSendAnalyticEvents.CommonSendSource, + override val accountFlow: StateFlow, + override val isAccountModeFlow: StateFlow, val blockClickEnableFlow: StateFlow, ) : SwapAmountComponentParams() } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index 8f43cade49..6d26842357 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -342,6 +342,8 @@ internal class SwapAmountModel @Inject constructor( appCurrency = appCurrency, swapDirection = swapDirection, clickIntents = this, + isAccountsMode = params.isAccountModeFlow.value, + account = params.accountFlow.value, ), ) }.launchIn(modelScope) @@ -367,6 +369,8 @@ internal class SwapAmountModel @Inject constructor( isBalanceHidden = params.isBalanceHidingFlow.value, showBestRateAnimation = showBestRateAnimation, isSingleWallet = isOnlyOneWallet, + isAccountsMode = params.isAccountModeFlow.value, + account = params.accountFlow.value, ), ) } @@ -384,38 +388,45 @@ internal class SwapAmountModel @Inject constructor( } private fun subscribeOnCryptoCurrencyStatusFlow() { - params.primaryCryptoCurrencyStatusFlow - .distinctUntilChanged { old, new -> old.value.amount == new.value.amount } // Check only balance changes - .onEach { primaryCurrencyStatus -> - val secondaryStatus = (uiState.value as? SwapAmountUM.Content)?.secondaryCryptoCurrencyStatus - initCurrencies( - primaryStatus = primaryCurrencyStatus, - secondaryStatus = secondaryStatus, + combine( + flow = params.primaryCryptoCurrencyStatusFlow.distinctUntilChanged { old, new -> + old.value.amount == new.value.amount + }, // Check only balance changes, + flow2 = params.accountFlow, + flow3 = params.isAccountModeFlow, + ) { primaryCurrencyStatus, account, isAccountsMode -> + val secondaryStatus = (uiState.value as? SwapAmountUM.Content)?.secondaryCryptoCurrencyStatus + initCurrencies( + primaryStatus = primaryCurrencyStatus, + secondaryStatus = secondaryStatus, + ) + if (secondaryStatus != null) { + uiState.transformerUpdate( + SwapAmountUpdateBalanceTransformer( + cryptoCurrencyStatus = primaryCurrencyStatus, + primaryMaximumAmountBoundary = primaryMaximumAmountBoundary, + primaryMinimumAmountBoundary = primaryMinimumAmountBoundary, + ), ) - if (secondaryStatus != null) { - uiState.transformerUpdate( - SwapAmountUpdateBalanceTransformer( - cryptoCurrencyStatus = primaryCurrencyStatus, - primaryMaximumAmountBoundary = primaryMaximumAmountBoundary, - primaryMinimumAmountBoundary = primaryMinimumAmountBoundary, - ), - ) - } else { - val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1 - uiState.transformerUpdate( - SwapAmountPrimaryReadyStateTransformer( - userWallet = userWallet, - primaryCryptoCurrencyStatus = primaryCurrencyStatus, - appCurrency = appCurrency, - swapDirection = swapDirection, - clickIntents = this, - isBalanceHidden = params.isBalanceHidingFlow.value, - showBestRateAnimation = showBestRateAnimation, - isSingleWallet = isOnlyOneWallet, - ), - ) - } - }.launchIn(modelScope) + } else { + val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1 + uiState.transformerUpdate( + SwapAmountPrimaryReadyStateTransformer( + userWallet = userWallet, + primaryCryptoCurrencyStatus = primaryCurrencyStatus, + appCurrency = appCurrency, + swapDirection = swapDirection, + clickIntents = this, + isBalanceHidden = params.isBalanceHidingFlow.value, + showBestRateAnimation = showBestRateAnimation, + isSingleWallet = isOnlyOneWallet, + isAccountsMode = isAccountsMode, + account = account, + ), + ) + } + }.flowOn(dispatchers.default) + .launchIn(modelScope) } private fun subscribeOnAmountUpdateTriggerUpdates() { @@ -523,6 +534,8 @@ internal class SwapAmountModel @Inject constructor( isBalanceHidden = params.isBalanceHidingFlow.value, showBestRateAnimation = showBestRateAnimation, isSingleWallet = isOnlyOneWallet, + isAccountsMode = params.isAccountModeFlow.value, + account = params.accountFlow.value, ), ) startLoadingQuotesTask(isSilentReload = false) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt index a490422d2f..1ed6aeea3c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt @@ -11,6 +11,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapDirection @@ -19,6 +20,7 @@ import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType import com.tangem.utils.StringsSigns.DOT +@Suppress("LongParameterList") internal class SwapAmountFieldConverter( private val swapDirection: SwapDirection, private val isBalanceHidden: Boolean, @@ -26,6 +28,8 @@ internal class SwapAmountFieldConverter( private val appCurrency: AppCurrency, private val clickIntents: AmountScreenClickIntents, private val isSingleWallet: Boolean, + @Suppress("UnusedPrivateProperty") private val isAccountsMode: Boolean, + @Suppress("UnusedPrivateProperty") private val account: Account.CryptoPortfolio?, ) { private val iconStateConverter = CryptoCurrencyToIconStateConverter() diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountBalanceHiddenTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountBalanceHiddenTransformer.kt index b6b0ccec59..3febeaf504 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountBalanceHiddenTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountBalanceHiddenTransformer.kt @@ -3,6 +3,7 @@ package com.tangem.features.swap.v2.impl.amount.model.transformers import com.tangem.common.ui.amountScreen.AmountScreenClickIntents import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.account.Account import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapDirection import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM @@ -11,6 +12,7 @@ import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import com.tangem.features.swap.v2.impl.amount.model.converter.SwapAmountFieldConverter import com.tangem.utils.transformer.Transformer +@Suppress("LongParameterList") internal class SwapAmountBalanceHiddenTransformer( private val isBalanceHidden: Boolean, private val isSingleWallet: Boolean, @@ -18,6 +20,8 @@ internal class SwapAmountBalanceHiddenTransformer( private val appCurrency: AppCurrency, private val swapDirection: SwapDirection, private val clickIntents: AmountScreenClickIntents, + private val isAccountsMode: Boolean, + private val account: Account.CryptoPortfolio?, ) : Transformer { override fun transform(prevState: SwapAmountUM): SwapAmountUM { @@ -30,6 +34,8 @@ internal class SwapAmountBalanceHiddenTransformer( appCurrency = appCurrency, clickIntents = clickIntents, isSingleWallet = isSingleWallet, + isAccountsMode = isAccountsMode, + account = account, ) val recalculatedPrimary = amountFieldConverter.convert( diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt index b6ff17ca44..9d39c5e8f4 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt @@ -3,6 +3,7 @@ package com.tangem.features.swap.v2.impl.amount.model.transformers import com.tangem.common.ui.amountScreen.AmountScreenClickIntents import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.express.models.ExpressRateType +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapCurrencies @@ -25,6 +26,8 @@ internal class SwapAmountPrimaryReadyStateTransformer( private val isBalanceHidden: Boolean, private val showBestRateAnimation: Boolean, private val isSingleWallet: Boolean, + private val isAccountsMode: Boolean, + private val account: Account.CryptoPortfolio?, ) : Transformer { private val amountFieldConverter = SwapAmountFieldConverter( @@ -34,6 +37,8 @@ internal class SwapAmountPrimaryReadyStateTransformer( appCurrency = appCurrency, clickIntents = clickIntents, isSingleWallet = isSingleWallet, + isAccountsMode = isAccountsMode, + account = account, ) override fun transform(prevState: SwapAmountUM): SwapAmountUM { diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt index da85780483..7d4a258917 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt @@ -3,6 +3,7 @@ package com.tangem.features.swap.v2.impl.amount.model.transformers import com.tangem.common.ui.amountScreen.AmountScreenClickIntents import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.express.models.ExpressRateType +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapCurrencies @@ -26,6 +27,8 @@ internal class SwapAmountSecondaryReadyStateTransformer( private val isBalanceHidden: Boolean, private val showBestRateAnimation: Boolean, private val isSingleWallet: Boolean, + private val isAccountsMode: Boolean, + private val account: Account.CryptoPortfolio?, ) : Transformer { private val amountFieldConverter = SwapAmountFieldConverter( @@ -35,6 +38,8 @@ internal class SwapAmountSecondaryReadyStateTransformer( appCurrency = appCurrency, clickIntents = clickIntents, isSingleWallet = isSingleWallet, + isAccountsMode = isAccountsMode, + account = account, ) override fun transform(prevState: SwapAmountUM): SwapAmountUM { diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt index 27c689d8a0..c26536b21c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt @@ -160,6 +160,8 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( userWallet = model.userWallet, filterProviderTypes = SEND_WITH_SWAP_PROVIDER_TYPES, analyticsSendSource = model.analyticsSendSource, + accountFlow = model.accountFlow, + isAccountModeFlow = model.isAccountModeFlow, ), ) } @@ -199,6 +201,8 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( analyticsCategoryName = model.analyticCategoryName, primaryCryptoCurrencyStatusFlow = model.primaryCryptoCurrencyStatusFlow, primaryFeePaidCurrencyStatusFlow = model.primaryFeePaidCurrencyStatusFlow, + accountFlow = model.accountFlow, + isAccountModeFlow = model.isAccountModeFlow, analyticsSendSource = model.analyticsSendSource, swapDirection = SwapDirection.Direct, ), diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt index b731648cfe..348a673fd5 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt @@ -10,6 +10,7 @@ import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet @@ -62,6 +63,8 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor( swapDirection = params.swapDirection, filterProviderTypes = SEND_WITH_SWAP_PROVIDER_TYPES, analyticsSendSource = params.analyticsSendSource, + accountFlow = params.accountFlow, + isAccountModeFlow = params.isAccountModeFlow, ), onResult = model::onAmountResult, onClick = model::showEditAmount, @@ -175,6 +178,8 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor( val isBalanceHidingFlow: StateFlow, val primaryCryptoCurrencyStatusFlow: StateFlow, val primaryFeePaidCurrencyStatusFlow: StateFlow, + val accountFlow: StateFlow, + val isAccountModeFlow: StateFlow, val callback: ModelCallback, ) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt index 811d403eac..357ffacf9c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt @@ -7,11 +7,15 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router +import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles +import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase +import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.express.models.ExpressError +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet @@ -50,7 +54,10 @@ internal class SendWithSwapModel @Inject constructor( private val getUserWalletUseCase: GetUserWalletUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, + private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase, + private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase, private val swapAlertFactory: SwapAlertFactory, + private val accountsFeatureToggles: AccountsFeatureToggles, paramsContainer: ParamsContainer, ) : Model(), SwapAmountComponent.ModelCallback, @@ -89,6 +96,12 @@ internal class SendWithSwapModel @Inject constructor( ), ) + val accountFlow: StateFlow + field = MutableStateFlow(null) + + val isAccountModeFlow: StateFlow + field = MutableStateFlow(false) + init { initUserWallet() initAppCurrency() @@ -192,36 +205,54 @@ internal class SendWithSwapModel @Inject constructor( val isSingleWalletWithToken = wallet is UserWallet.Cold && wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() - getCurrencyStatus( - cryptoCurrency = cryptoCurrency, - isSingleWalletWithToken = isSingleWalletWithToken, - isMultiCurrency = isMultiCurrency, - ).onEach { maybeCryptoCurrency -> - maybeCryptoCurrency.fold( - ifRight = { cryptoCurrencyStatus -> - primaryCryptoCurrencyStatusFlow.value = cryptoCurrencyStatus - primaryFeePaidCurrencyStatusFlow.value = getFeePaidCryptoCurrencyStatusSyncUseCase( - userWalletId = params.userWalletId, - cryptoCurrencyStatus = cryptoCurrencyStatus, - ).getOrNull() ?: cryptoCurrencyStatus - }, - ifLeft = { error -> - swapAlertFactory.getGenericErrorState( - expressError = ExpressError.UnknownError, - onFailedTxEmailClick = { - modelScope.launch { - swapAlertFactory.onFailedTxEmailClick( - userWallet = userWallet, - cryptoCurrency = params.currency, - errorMessage = error.toString(), - ) - } - }, - popBack = ::onBackClick, - ) - }, - ) - }.launchIn(modelScope) + if (accountsFeatureToggles.isFeatureEnabled) { + getAccountCurrencyStatusUseCase( + userWalletId = params.userWalletId, + currency = cryptoCurrency, + ).onEach { (account, cryptoCurrencyStatus) -> + accountFlow.value = account + isAccountModeFlow.value = isAccountsModeEnabledUseCase.invokeSync() + + primaryCryptoCurrencyStatusFlow.value = cryptoCurrencyStatus + primaryFeePaidCurrencyStatusFlow.value = getFeePaidCryptoCurrencyStatusSyncUseCase( + userWalletId = params.userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + ).getOrNull() ?: cryptoCurrencyStatus + }.flowOn(dispatchers.default) + .launchIn(modelScope) + } else { + getCurrencyStatus( + cryptoCurrency = cryptoCurrency, + isSingleWalletWithToken = isSingleWalletWithToken, + isMultiCurrency = isMultiCurrency, + ).onEach { maybeCryptoCurrency -> + maybeCryptoCurrency.fold( + ifRight = { cryptoCurrencyStatus -> + primaryCryptoCurrencyStatusFlow.value = cryptoCurrencyStatus + primaryFeePaidCurrencyStatusFlow.value = getFeePaidCryptoCurrencyStatusSyncUseCase( + userWalletId = params.userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + ).getOrNull() ?: cryptoCurrencyStatus + }, + ifLeft = { error -> + swapAlertFactory.getGenericErrorState( + expressError = ExpressError.UnknownError, + onFailedTxEmailClick = { + modelScope.launch { + swapAlertFactory.onFailedTxEmailClick( + userWallet = userWallet, + cryptoCurrency = params.currency, + errorMessage = error.toString(), + ) + } + }, + popBack = ::onBackClick, + ) + }, + ) + }.flowOn(dispatchers.default) + .launchIn(modelScope) + } } private fun getCurrencyStatus(