diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt index 072a7389f6..b377102b35 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt @@ -66,10 +66,12 @@ fun NotificationWithBackground(config: NotificationConfig, modifier: Modifier = Image( painter = painterResource(config.iconResId), contentDescription = null, - modifier = Modifier.constrainAs(iconRef) { - start.linkTo(parent.start, spacing12) - linkTo(titleRef.top, subtitleRef.bottom, bias = 0.1f) - }, + modifier = Modifier + .size(TangemTheme.dimens.size34) + .constrainAs(iconRef) { + start.linkTo(parent.start, spacing12) + linkTo(titleRef.top, subtitleRef.bottom, bias = 0.1f) + }, ) Text( text = config.title.resolveReference(), @@ -147,7 +149,7 @@ private class NotificationWithBackgroundPreviewProvider : PreviewParameterProvid NotificationConfig( title = resourceReference(id = R.string.main_swap_promotion_title), subtitle = resourceReference(id = R.string.main_swap_promotion_message), - iconResId = R.drawable.ic_swap_promo_34, + iconResId = R.drawable.img_swap_promo, backgroundResId = R.drawable.img_swap_promo_banner_background, ), NotificationConfig( @@ -158,7 +160,7 @@ private class NotificationWithBackgroundPreviewProvider : PreviewParameterProvid "currencies between any chains you wish. Commission free period " + "till Dec 31.", ), - iconResId = R.drawable.ic_swap_promo_34, + iconResId = R.drawable.img_swap_promo, backgroundResId = R.drawable.img_swap_promo_banner_background, buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( text = resourceReference(id = R.string.token_swap_promotion_button), diff --git a/core/ui/src/main/res/drawable/ic_swap_promo_34.xml b/core/ui/src/main/res/drawable/ic_swap_promo_34.xml deleted file mode 100644 index 3c279910c8..0000000000 --- a/core/ui/src/main/res/drawable/ic_swap_promo_34.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/core/ui/src/main/res/drawable/img_swap_promo.webp b/core/ui/src/main/res/drawable/img_swap_promo.webp new file mode 100644 index 0000000000..4f5bd02d24 Binary files /dev/null and b/core/ui/src/main/res/drawable/img_swap_promo.webp differ diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt index f658706441..3090438e08 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt @@ -16,6 +16,8 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.swap.domain.api.SwapRepository import com.tangem.feature.swap.domain.models.domain.LeastTokenInfo import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.coroutines.runCatching +import com.tangem.utils.isNullOrZero import kotlinx.coroutines.flow.* import java.math.BigDecimal @@ -89,30 +91,36 @@ class GetCurrencyWarningsUseCase( cryptoStatuses.fold( ifLeft = { null }, ifRight = { cryptoCurrencyStatuses -> - val pairs = swapRepository.getPairs( - LeastTokenInfo( - contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress ?: "0", - network = currency.network.backendId, - ), - cryptoCurrencyStatuses.map { it.currency }, - ) + val pairs = runCatching(dispatchers.io) { + swapRepository.getPairs( + LeastTokenInfo( + contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress ?: "0", + network = currency.network.backendId, + ), + cryptoCurrencyStatuses.map { it.currency }, + ) + }.getOrNull()?.pairs ?: emptyList() + val filteredCurrencies = cryptoCurrencyStatuses.filterNot { - it.currency.network.backendId == currency.network.backendId + it.currency.id == currency.id } - val currencyPairs = pairs.pairs.filter { + val currencyPairs = pairs.filter { it.from.network == currency.network.backendId || it.to.network == currency.network.backendId } val isExchangeable = currencyPairs.any { pair -> - val availablePair = if (currencyStatus.value.amount.isZero()) { - filteredCurrencies.filterNot { it.value.amount.isZero() } + val availablePair = if (currencyStatus.value.amount.isNullOrZero()) { + filteredCurrencies.filterNot { it.value.amount.isNullOrZero() } } else { filteredCurrencies } availablePair .any { - it.currency.network.backendId == pair.to.network || - it.currency.network.backendId == pair.from.network + ( + it.currency.network.backendId == pair.to.network || + it.currency.network.backendId == pair.from.network + ) && + !it.value.amount.isNullOrZero() } } if (isExchangeable) { diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 3754e0c835..631d3e6d51 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -102,15 +102,15 @@ internal class SwapInteractorImpl @Inject constructor( currency = currency, leastPairs = pairsLeast.pairs, cryptoCurrenciesList = walletCurrencyStatusesExceptInitial, - tokenInfoForFilter = { it.from }, - tokenInfoForAvailable = { it.to }, + tokenInfoForFilter = { it.to }, + tokenInfoForAvailable = { it.from }, ), toGroup = getToCurrenciesGroup( currency = currency, leastPairs = pairsLeast.pairs, cryptoCurrenciesList = walletCurrencyStatusesExceptInitial, - tokenInfoForFilter = { it.to }, - tokenInfoForAvailable = { it.from }, + tokenInfoForFilter = { it.from }, + tokenInfoForAvailable = { it.to }, ), allProviders = pairsLeast.allProviders, ) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt index 6f42bbd1d2..895fcb1ea5 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt @@ -2,10 +2,7 @@ package com.tangem.feature.swap.converters import com.tangem.common.Provider import com.tangem.core.ui.components.currency.tokenicon.TokenIconState -import com.tangem.core.ui.extensions.getTintForTokenIcon -import com.tangem.core.ui.extensions.networkIconResId -import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon +import com.tangem.core.ui.extensions.* import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrency @@ -15,6 +12,7 @@ import com.tangem.feature.swap.domain.models.ui.CurrenciesGroup import com.tangem.feature.swap.models.SwapSelectTokenStateHolder import com.tangem.feature.swap.models.TokenBalanceData import com.tangem.feature.swap.models.TokenToSelectState +import com.tangem.feature.swap.presentation.R import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.toImmutableList @@ -26,8 +24,12 @@ class TokensDataConverter( ) : Converter { override fun convert(value: CurrenciesGroup): SwapSelectTokenStateHolder { - val availableTitle = TokenToSelectState.Title(stringReference("My tokens")) // todo replace with resource - val unavailableTitle = TokenToSelectState.Title(stringReference("My tokens")) // todo replace with resource + val availableTitle = TokenToSelectState.Title( + resourceReference(R.string.exchange_tokens_available_tokens_header), + ) + val unavailableTitle = TokenToSelectState.Title( + resourceReference(R.string.exchange_tokens_available_tokens_header), + ) return SwapSelectTokenStateHolder( availableTokens = value.available.map { tokenWithBalanceToTokenToSelect(it, true) } .toMutableList() diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt index ae4d9ba3cf..1502568f92 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt @@ -19,7 +19,7 @@ data class SwapStateHolder( val blockchainId: String, // not the same as networkId, its local id in app val warnings: List = emptyList(), val alert: SwapWarning.GenericWarning? = null, - val updateInProgress: Boolean = false, + val changeCardsButtonState: ChangeCardsButtonState = ChangeCardsButtonState.ENABLED, val providerState: ProviderState, val fee: FeeItemState = FeeItemState.Empty, @@ -131,4 +131,8 @@ sealed interface SwapWarning { enum class GenericWarningType { NETWORK, OTHER +} + +enum class ChangeCardsButtonState { + ENABLED, DISABLED, UPDATE_IN_PROGRESS } \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 18933ba236..e66df2962d 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -84,7 +84,7 @@ internal class StateBuilder( onBackClicked = actions.onBackClicked, onChangeCardsClicked = actions.onChangeCardsClicked, onMaxAmountSelected = actions.onMaxAmountSelected, - updateInProgress = true, + changeCardsButtonState = ChangeCardsButtonState.UPDATE_IN_PROGRESS, onShowPermissionBottomSheet = actions.openPermissionBottomSheet, providerState = ProviderState.Empty(), ) @@ -138,7 +138,7 @@ internal class StateBuilder( loading = false, onClick = { }, ), - updateInProgress = false, + changeCardsButtonState = ChangeCardsButtonState.DISABLED, ) } @@ -186,7 +186,7 @@ internal class StateBuilder( swapButton = SwapButton(enabled = false, loading = true, onClick = {}), providerState = ProviderState.Loading(), permissionState = uiStateHolder.permissionState, - updateInProgress = true, + changeCardsButtonState = ChangeCardsButtonState.UPDATE_IN_PROGRESS, ) } @@ -207,6 +207,7 @@ internal class StateBuilder( bestRatedProviderId: String, isManyProviders: Boolean, selectedFeeType: FeeType, + isReverseSwapPossible: Boolean, ): SwapStateHolder { if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder @@ -257,7 +258,11 @@ internal class StateBuilder( loading = false, onClick = actions.onSwapClick, ), - updateInProgress = false, + changeCardsButtonState = if (isReverseSwapPossible) { + ChangeCardsButtonState.ENABLED + } else { + ChangeCardsButtonState.DISABLED + }, providerState = swapProvider.convertToContentClickableProviderState( isBestRate = bestRatedProviderId == swapProvider.providerId, fromTokenInfo = quoteModel.fromTokenInfo, @@ -354,12 +359,14 @@ internal class StateBuilder( } } + @Suppress("LongParameterList") fun createQuotesErrorState( uiStateHolder: SwapStateHolder, swapProvider: SwapProvider, fromToken: TokenSwapInfo, toToken: CryptoCurrencyStatus?, dataError: DataError, + isReverseSwapPossible: Boolean, ): SwapStateHolder { if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder @@ -409,7 +416,11 @@ internal class StateBuilder( loading = false, onClick = actions.onSwapClick, ), - updateInProgress = false, + changeCardsButtonState = if (isReverseSwapPossible) { + ChangeCardsButtonState.ENABLED + } else { + ChangeCardsButtonState.DISABLED + }, providerState = providerState, ) } @@ -475,6 +486,7 @@ internal class StateBuilder( fun createQuotesEmptyAmountState( uiStateHolder: SwapStateHolder, emptyAmountState: SwapState.EmptyAmountState, + isReverseSwapPossible: Boolean, ): SwapStateHolder { if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder @@ -514,7 +526,11 @@ internal class StateBuilder( loading = false, onClick = { }, ), - updateInProgress = false, + changeCardsButtonState = if (isReverseSwapPossible) { + ChangeCardsButtonState.ENABLED + } else { + ChangeCardsButtonState.DISABLED + }, providerState = ProviderState.Empty(), ) } @@ -538,7 +554,7 @@ internal class StateBuilder( fun createSilentLoadState(uiState: SwapStateHolder): SwapStateHolder { return uiState.copy( - updateInProgress = true, + changeCardsButtonState = ChangeCardsButtonState.UPDATE_IN_PROGRESS, ) } @@ -713,7 +729,7 @@ internal class StateBuilder( onClick = onAlertClick, type = if (txState is TxState.NetworkError) GenericWarningType.NETWORK else GenericWarningType.OTHER, ), - updateInProgress = false, + changeCardsButtonState = ChangeCardsButtonState.ENABLED, ) } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt index 8d1fe2e6df..360cd4e016 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt @@ -195,23 +195,34 @@ private fun SwapButton(state: SwapStateHolder, modifier: Modifier = Modifier) { contentColor = TangemTheme.colors.text.primary1, modifier = modifier.size(TangemTheme.dimens.size48), onClick = state.onChangeCardsClicked, - enabled = !state.updateInProgress, + enabled = state.changeCardsButtonState == ChangeCardsButtonState.ENABLED, ) { - if (state.updateInProgress) { - CircularProgressIndicator( - modifier = Modifier - .size(TangemTheme.dimens.size16) - .padding(TangemTheme.dimens.spacing14), - color = TangemTheme.colors.icon.primary1, - strokeWidth = TangemTheme.dimens.size2, - ) - } else { - Icon( - painter = painterResource(id = R.drawable.ic_exchange_vertical_24), - contentDescription = null, - tint = TangemTheme.colors.text.primary1, - modifier = Modifier.padding(TangemTheme.dimens.spacing12), - ) + when (state.changeCardsButtonState) { + ChangeCardsButtonState.UPDATE_IN_PROGRESS -> { + CircularProgressIndicator( + modifier = Modifier + .size(TangemTheme.dimens.size16) + .padding(TangemTheme.dimens.spacing14), + color = TangemTheme.colors.icon.primary1, + strokeWidth = TangemTheme.dimens.size2, + ) + } + ChangeCardsButtonState.ENABLED -> { + Icon( + painter = painterResource(id = R.drawable.ic_exchange_vertical_24), + contentDescription = null, + tint = TangemTheme.colors.text.primary1, + modifier = Modifier.padding(TangemTheme.dimens.spacing12), + ) + } + ChangeCardsButtonState.DISABLED -> { + Icon( + painter = painterResource(id = R.drawable.ic_exchange_vertical_24), + contentDescription = null, + tint = TangemTheme.colors.text.disabled, + modifier = Modifier.padding(TangemTheme.dimens.spacing12), + ) + } } } } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index a36badc51e..b7606f80d0 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -60,8 +60,8 @@ internal class SwapViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver { - private val initialCryptoCurrency: CryptoCurrency = savedStateHandle[SwapFragment.CURRENCY_BUNDLE_KEY] - ?: error("no expected parameter CryptoCurrency found`") + private val initialCryptoCurrency: CryptoCurrency = + savedStateHandle[SwapFragment.CURRENCY_BUNDLE_KEY] ?: error("no expected parameter CryptoCurrency found`") private lateinit var initialCryptoCurrencyStatus: CryptoCurrencyStatus private var isBalanceHidden = true @@ -114,15 +114,12 @@ internal class SwapViewModel @Inject constructor( } override fun onCreate(owner: LifecycleOwner) { - getBalanceHidingSettingsUseCase() - .flowWithLifecycle(owner.lifecycle) - .onEach { - isBalanceHidden = it.isBalanceHidden - withContext(dispatchers.main) { - uiState = stateBuilder.updateBalanceHiddenState(uiState, isBalanceHidden) - } + getBalanceHidingSettingsUseCase().flowWithLifecycle(owner.lifecycle).onEach { + isBalanceHidden = it.isBalanceHidden + withContext(dispatchers.main) { + uiState = stateBuilder.updateBalanceHiddenState(uiState, isBalanceHidden) } - .launchIn(viewModelScope) + }.launchIn(viewModelScope) } override fun onCleared() { @@ -175,19 +172,18 @@ internal class SwapViewModel @Inject constructor( selectedCurrency = null, ) - uiState = - stateBuilder.createInitialErrorState( - uiState, - (it as? ExpressException)?.dataError?.code ?: DataError.UnknownError.code, - ) { - uiState = stateBuilder.createInitialLoadingState( - initialCurrency = initialCryptoCurrency, - networkInfo = blockchainInteractor.getBlockchainInfo( - initialCryptoCurrency.network.backendId, - ), - ) - initTokens() - } + uiState = stateBuilder.createInitialErrorState( + uiState, + (it as? ExpressException)?.dataError?.code ?: DataError.UnknownError.code, + ) { + uiState = stateBuilder.createInitialLoadingState( + initialCurrency = initialCryptoCurrency, + networkInfo = blockchainInteractor.getBlockchainInfo( + initialCryptoCurrency.network.backendId, + ), + ) + initTokens() + } } } } @@ -216,7 +212,7 @@ internal class SwapViewModel @Inject constructor( } private fun updateTokensState(dataState: TokensDataStateExpress) { - val tokensDataState = if (!isOrderReversed) dataState.toGroup else dataState.fromGroup + val tokensDataState = if (isOrderReversed) dataState.fromGroup else dataState.toGroup uiState = stateBuilder.addTokensToState( uiState = uiState, tokensDataState = tokensDataState, @@ -295,8 +291,7 @@ internal class SwapViewModel @Inject constructor( if (providersState.isNotEmpty()) { val (provider, state) = updateLoadedQuotes(providersState) setupLoadedState(provider, state, fromToken) - val successStates = providersState - .getLastLoadedSuccessStates() + val successStates = providersState.getLastLoadedSuccessStates() val pricesLowerBest = getPricesLowerBest(successStates) uiState = stateBuilder.updateProvidersBottomSheetContent( uiState = uiState, @@ -329,6 +324,7 @@ internal class SwapViewModel @Inject constructor( bestRatedProviderId = bestRatedProviderId, isManyProviders = dataState.lastLoadedSwapStates.size > 1, selectedFeeType = dataState.selectedFee?.feeType ?: FeeType.NORMAL, + isReverseSwapPossible = isReverseSwapPossible(), ) if (uiState.warnings.any { it is SwapWarning.UnableToCoverFeeWarning }) { analyticsEventHandler.send( @@ -343,6 +339,7 @@ internal class SwapViewModel @Inject constructor( uiState = stateBuilder.createQuotesEmptyAmountState( uiStateHolder = uiState, emptyAmountState = state, + isReverseSwapPossible = isReverseSwapPossible(), ) } is SwapState.SwapError -> { @@ -353,6 +350,7 @@ internal class SwapViewModel @Inject constructor( fromToken = state.fromTokenInfo, toToken = dataState.toCryptoCurrency, dataError = state.error, + isReverseSwapPossible = isReverseSwapPossible(), ) sendErrorAnalyticsEvent(state.error, provider) } @@ -419,16 +417,15 @@ internal class SwapViewModel @Inject constructor( } private fun selectDefaultFee(state: SwapState.QuotesLoadedState): TxFee? { - return dataState.selectedFee - ?: when (val txFee = state.txFee) { - TxFeeState.Empty -> null - is TxFeeState.MultipleFeeState -> { - txFee.normalFee - } - is TxFeeState.SingleFeeState -> { - txFee.fee - } + return dataState.selectedFee ?: when (val txFee = state.txFee) { + TxFeeState.Empty -> null + is TxFeeState.MultipleFeeState -> { + txFee.normalFee } + is TxFeeState.SingleFeeState -> { + txFee.fee + } + } } @Suppress("LongMethod") @@ -453,58 +450,56 @@ internal class SwapViewModel @Inject constructor( includeFeeInAmount = lastLoadedQuotesState.preparedSwapConfigState.includeFeeInAmount, fee = requireNotNull(dataState.selectedFee), ) - } - .onSuccess { - when (it) { - is TxState.TxSent -> { - val url = blockchainInteractor.getExplorerTransactionLink( - networkId = dataState.networkId, - txAddress = it.txAddress, - ) - uiState = stateBuilder.createSuccessState( - uiState = uiState, - txState = it, - dataState = dataState, - txUrl = url, - onExploreClick = { - val txHash = it.txAddress - if (txHash.isNotEmpty()) { - swapRouter.openUrl(url) - } + }.onSuccess { + when (it) { + is TxState.TxSent -> { + val url = blockchainInteractor.getExplorerTransactionLink( + networkId = dataState.networkId, + txAddress = it.txAddress, + ) + uiState = stateBuilder.createSuccessState( + uiState = uiState, + txState = it, + dataState = dataState, + txUrl = url, + onExploreClick = { + val txHash = it.txAddress + if (txHash.isNotEmpty()) { + swapRouter.openUrl(url) + } + analyticsEventHandler.send( + event = SwapEvents.ButtonExplore(initialCryptoCurrency.symbol), + ) + }, + onStatusClick = { + val txExternalUrl = it.txExternalUrl + if (!txExternalUrl.isNullOrBlank()) { + swapRouter.openUrl(txExternalUrl) analyticsEventHandler.send( - event = SwapEvents.ButtonExplore(initialCryptoCurrency.symbol), + event = SwapEvents.ButtonStatus(initialCryptoCurrency.symbol), ) - }, - onStatusClick = { - val txExternalUrl = it.txExternalUrl - if (!txExternalUrl.isNullOrBlank()) { - swapRouter.openUrl(txExternalUrl) - analyticsEventHandler.send( - event = SwapEvents.ButtonStatus(initialCryptoCurrency.symbol), - ) - } - }, - ) - sendSuccessEvent() + } + }, + ) + sendSuccessEvent() - swapRouter.openScreen(SwapNavScreen.Success) - } - is TxState.UserCancelled -> { - startLoadingQuotesFromLastState() - } - else -> { - startLoadingQuotesFromLastState() - uiState = stateBuilder.createErrorTransaction(uiState, it) { - uiState = stateBuilder.clearAlert(uiState) - } + swapRouter.openScreen(SwapNavScreen.Success) + } + is TxState.UserCancelled -> { + startLoadingQuotesFromLastState() + } + else -> { + startLoadingQuotesFromLastState() + uiState = stateBuilder.createErrorTransaction(uiState, it) { + uiState = stateBuilder.clearAlert(uiState) } } } - .onFailure { - Timber.e(it) - startLoadingQuotesFromLastState() - makeDefaultAlert() - } + }.onFailure { + Timber.e(it) + startLoadingQuotesFromLastState() + makeDefaultAlert() + } } } @@ -540,8 +535,7 @@ internal class SwapViewModel @Inject constructor( "dataState.approveDataModel might not be null" }, forTokenContractAddress = (dataState.fromCryptoCurrency?.currency as? CryptoCurrency.Token) - ?.contractAddress - ?: "", + ?.contractAddress ?: "", fromToken = requireNotNull(dataState.fromCryptoCurrency?.currency) { "dataState.fromCurrency might not be null" }, @@ -554,25 +548,23 @@ internal class SwapViewModel @Inject constructor( }, ), ) - } - .onSuccess { - when (it) { - is TxState.TxSent -> { - uiState = stateBuilder.loadingPermissionState(uiState) - uiState = stateBuilder.dismissBottomSheet(uiState) - startLoadingQuotesFromLastState(isSilent = true) - } - is TxState.UserCancelled -> Unit - else -> { - uiState = stateBuilder.createErrorTransaction(uiState, it) { - uiState = stateBuilder.clearAlert(uiState) - } + }.onSuccess { + when (it) { + is TxState.TxSent -> { + uiState = stateBuilder.loadingPermissionState(uiState) + uiState = stateBuilder.dismissBottomSheet(uiState) + startLoadingQuotesFromLastState(isSilent = true) + } + is TxState.UserCancelled -> Unit + else -> { + uiState = stateBuilder.createErrorTransaction(uiState, it) { + uiState = stateBuilder.clearAlert(uiState) } } } - .onFailure { - makeDefaultAlert() - } + }.onFailure { + makeDefaultAlert() + } } } @@ -653,12 +645,17 @@ internal class SwapViewModel @Inject constructor( private fun onChangeCardsClicked() { val newFromToken = dataState.toCryptoCurrency val newToToken = dataState.fromCryptoCurrency + if (newFromToken != null && newToToken != null) { dataState = dataState.copy( fromCryptoCurrency = newFromToken, toCryptoCurrency = newToToken, ) isOrderReversed = !isOrderReversed + dataState.tokensDataState?.let { + updateTokensState(it) + } + val decimals = newFromToken.currency.decimals lastAmount.value = cutAmountWithDecimals(decimals, lastAmount.value) uiState = stateBuilder.updateSwapAmount( @@ -783,8 +780,8 @@ internal class SwapViewModel @Inject constructor( }, onClickFee = { val selectedFee = dataState.selectedFee?.feeType ?: FeeType.NORMAL - val txFeeState = dataState.getCurrentLoadedSwapState()?.txFee as? TxFeeState.MultipleFeeState - ?: return@UiActions + val txFeeState = + dataState.getCurrentLoadedSwapState()?.txFee as? TxFeeState.MultipleFeeState ?: return@UiActions uiState = stateBuilder.showSelectFeeBottomSheet( uiState = uiState, selectedFee = selectedFee, @@ -862,9 +859,7 @@ internal class SwapViewModel @Inject constructor( private fun findBestQuoteProvider(state: SuccessLoadedSwapData): SwapProvider? { // finding best quotes return state.minByOrNull { - if (!it.value.fromTokenInfo.amountFiat.isNullOrZero() && - !it.value.toTokenInfo.amountFiat.isNullOrZero() - ) { + if (!it.value.fromTokenInfo.amountFiat.isNullOrZero() && !it.value.toTokenInfo.amountFiat.isNullOrZero()) { it.value.fromTokenInfo.amountFiat.divide( it.value.toTokenInfo.amountFiat, it.value.toTokenInfo.cryptoCurrencyStatus.currency.decimals, @@ -894,15 +889,13 @@ internal class SwapViewModel @Inject constructor( } private fun createSelectedAppCurrencyFlow(): StateFlow { - return getSelectedAppCurrencyUseCase() - .map { maybeAppCurrency -> - maybeAppCurrency.getOrElse { AppCurrency.Default } - } - .stateIn( - scope = viewModelScope, - started = SharingStarted.Eagerly, - initialValue = AppCurrency.Default, - ) + return getSelectedAppCurrencyUseCase().map { maybeAppCurrency -> + maybeAppCurrency.getOrElse { AppCurrency.Default } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = AppCurrency.Default, + ) } private fun findSwapProviders(fromToken: CryptoCurrencyStatus, toToken: CryptoCurrencyStatus): List { @@ -931,11 +924,27 @@ internal class SwapViewModel @Inject constructor( } private fun Map.getLastLoadedSuccessStates(): SuccessLoadedSwapData { - return this - .filter { it.value is SwapState.QuotesLoadedState } + return this.filter { it.value is SwapState.QuotesLoadedState } .mapValues { it.value as SwapState.QuotesLoadedState } } + private fun isReverseSwapPossible(): Boolean { + val from = dataState.fromCryptoCurrency ?: return false + val to = dataState.toCryptoCurrency ?: return false + + val currenciesGroup = if (isOrderReversed) { + dataState.tokensDataState?.toGroup + } else { + dataState.tokensDataState?.fromGroup + } ?: return false + + val chosen = if (isOrderReversed) from else to + + return currenciesGroup.available + .map { it.currencyStatus.currency } + .contains(chosen.currency) + } + companion object { private const val loggingTag = "SwapViewModel" private const val INITIAL_AMOUNT = "" diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt index fe9ef56abc..dbe63a69f8 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt @@ -46,7 +46,7 @@ internal sealed class TokenDetailsNotification(val config: NotificationConfig) { config = NotificationConfig( title = resourceReference(id = R.string.token_swap_promotion_title), subtitle = resourceReference(id = R.string.token_swap_promotion_message), - iconResId = R.drawable.ic_swap_promo_34, + iconResId = R.drawable.img_swap_promo, backgroundResId = R.drawable.img_swap_promo_banner_background, onCloseClick = onCloseClick, buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt index 6aeebbc6c7..7f6bfd6b46 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt @@ -173,7 +173,7 @@ sealed class WalletNotification(val config: NotificationConfig) { config = NotificationConfig( title = resourceReference(id = R.string.main_swap_promotion_title), subtitle = resourceReference(id = R.string.main_swap_promotion_message), - iconResId = R.drawable.ic_swap_promo_34, + iconResId = R.drawable.img_swap_promo, backgroundResId = R.drawable.img_swap_promo_banner_background, onCloseClick = onCloseClick, ),