From b024f91f192f4a5af2bc1335294eb0a49c669c34 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Dec 2023 16:12:47 +0300 Subject: [PATCH 01/17] Updated on 2026-08-14 --- .../src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 b7c7418dae..326f09752c 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 @@ -302,6 +302,7 @@ internal class StateBuilder( ) } + @Suppress("LongMethod", "CyclomaticComplexMethod") private fun getWarningsForSuccessState( quoteModel: SwapState.QuotesLoadedState, fromToken: CryptoCurrency, @@ -327,7 +328,8 @@ internal class StateBuilder( else -> Unit } if (!quoteModel.preparedSwapConfigState.isFeeEnough && - quoteModel.preparedSwapConfigState.isBalanceEnough + quoteModel.preparedSwapConfigState.isBalanceEnough && + quoteModel.permissionState !is PermissionDataState.PermissionLoading ) { warnings.add( SwapWarning.UnableToCoverFeeWarning( From 7507f59feec1cccc3e0f88fce481741ab21c1ec1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Dec 2023 16:55:43 +0300 Subject: [PATCH 02/17] Updated on 2026-08-14 --- .../com/tangem/feature/swap/viewmodels/SwapViewModel.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 955d5e5f74..34f4e0f818 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 @@ -853,14 +853,10 @@ internal class SwapViewModel @Inject constructor( startLoadingQuotesFromLastState() }, onPolicyClick = { - uiState = stateBuilder.showWebViewBottomSheet(uiState, it) { - uiState = stateBuilder.dismissBottomSheet(uiState) - } + swapRouter.openUrl(it) }, onTosClick = { - uiState = stateBuilder.showWebViewBottomSheet(uiState, it) { - uiState = stateBuilder.dismissBottomSheet(uiState) - } + swapRouter.openUrl(it) }, ) } From a1943321d9d97d7de0d82fe4e76ed6864f3ef542 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Dec 2023 18:25:48 +0300 Subject: [PATCH 03/17] Updated on 2026-08-14 --- .../tangem/feature/swap/viewmodels/SwapViewModel.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) 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 34f4e0f818..23f24834f8 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 @@ -420,16 +420,21 @@ internal class SwapViewModel @Inject constructor( } else { dataState.copy( swapDataModel = swapDataModel, - selectedFee = selectDefaultFee(state), + selectedFee = updateOrSelectFee(state), ) } } - private fun selectDefaultFee(state: SwapState.QuotesLoadedState): TxFee? { - return dataState.selectedFee ?: when (val txFee = state.txFee) { + private fun updateOrSelectFee(state: SwapState.QuotesLoadedState): TxFee? { + val selectedFeeType = dataState.selectedFee?.feeType ?: FeeType.NORMAL + return when (val txFee = state.txFee) { TxFeeState.Empty -> null is TxFeeState.MultipleFeeState -> { - txFee.normalFee + if (selectedFeeType == FeeType.NORMAL) { + txFee.normalFee + } else { + txFee.priorityFee + } } is TxFeeState.SingleFeeState -> { txFee.fee From 6fe140bb45cc6f9e708961ab26d52e5df6144d82 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Dec 2023 18:45:36 +0300 Subject: [PATCH 04/17] Updated on 2026-08-14 --- .../tangem/feature/swap/ui/StateBuilder.kt | 42 +++++++++++-------- .../feature/swap/viewmodels/SwapViewModel.kt | 12 ++++-- 2 files changed, 33 insertions(+), 21 deletions(-) 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 326f09752c..dc89739051 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 @@ -209,7 +209,7 @@ internal class StateBuilder( fromToken: CryptoCurrency, swapProvider: SwapProvider, bestRatedProviderId: String, - isManyProviders: Boolean, + isNeedBestRateBadge: Boolean, selectedFeeType: FeeType, isReverseSwapPossible: Boolean, ): SwapStateHolder { @@ -270,7 +270,7 @@ internal class StateBuilder( isBestRate = bestRatedProviderId == swapProvider.providerId, fromTokenInfo = quoteModel.fromTokenInfo, toTokenInfo = quoteModel.toTokenInfo, - isNeedBadge = isManyProviders, + isNeedBestRateBadge = isNeedBestRateBadge, selectionType = ProviderState.SelectionType.CLICK, onProviderClick = actions.onProviderClick, ), @@ -327,19 +327,7 @@ internal class StateBuilder( ) else -> Unit } - if (!quoteModel.preparedSwapConfigState.isFeeEnough && - quoteModel.preparedSwapConfigState.isBalanceEnough && - quoteModel.permissionState !is PermissionDataState.PermissionLoading - ) { - warnings.add( - SwapWarning.UnableToCoverFeeWarning( - createUnableToCoverFeeNotificationConfig( - fromToken = fromToken, - onBuyClick = actions.onBuyClick, - ), - ), - ) - } + addUnableCoverFeeWarning(quoteModel, fromToken, warnings) // check isBalanceEnough, but for dex includeFeeInAmount always Excluded if (!quoteModel.preparedSwapConfigState.isBalanceEnough && quoteModel.preparedSwapConfigState.includeFeeInAmount !is IncludeFeeInAmount.Included @@ -379,6 +367,26 @@ internal class StateBuilder( return warnings } + private fun addUnableCoverFeeWarning( + quoteModel: SwapState.QuotesLoadedState, + fromToken: CryptoCurrency, + warnings: MutableList, + ) { + if (!quoteModel.preparedSwapConfigState.isFeeEnough && + quoteModel.preparedSwapConfigState.isBalanceEnough && + quoteModel.permissionState !is PermissionDataState.PermissionLoading + ) { + warnings.add( + SwapWarning.UnableToCoverFeeWarning( + createUnableToCoverFeeNotificationConfig( + fromToken = fromToken, + onBuyClick = actions.onBuyClick, + ), + ), + ) + } + } + private fun getSwapButtonEnabled(preparedSwapConfigState: PreparedSwapConfigState): Boolean { if (preparedSwapConfigState.hasOutgoingTransaction) return false return when (preparedSwapConfigState.includeFeeInAmount) { @@ -1123,7 +1131,7 @@ internal class StateBuilder( fromTokenInfo: TokenSwapInfo, toTokenInfo: TokenSwapInfo, selectionType: ProviderState.SelectionType, - isNeedBadge: Boolean, + isNeedBestRateBadge: Boolean, onProviderClick: (String) -> Unit, ): ProviderState { val rate = toTokenInfo.tokenAmount.value.calculateRate( @@ -1133,7 +1141,7 @@ internal class StateBuilder( val fromCurrencySymbol = fromTokenInfo.cryptoCurrencyStatus.currency.symbol val toCurrencySymbol = toTokenInfo.cryptoCurrencyStatus.currency.symbol val rateString = "1 $fromCurrencySymbol ≈ $rate $toCurrencySymbol" - val badge = if (isNeedBadge && isBestRate) { + val badge = if (isNeedBestRateBadge && isBestRate) { ProviderState.AdditionalBadge.BestTrade } else { ProviderState.AdditionalBadge.Empty 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 23f24834f8..8dc1e1a382 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 @@ -326,7 +326,7 @@ internal class SwapViewModel @Inject constructor( fromToken = fromToken.currency, swapProvider = provider, bestRatedProviderId = bestRatedProviderId, - isManyProviders = dataState.lastLoadedSwapStates.size > 1, + isNeedBestRateBadge = dataState.lastLoadedSwapStates.consideredProvidersStates().size > 1, selectedFeeType = dataState.selectedFee?.feeType ?: FeeType.NORMAL, isReverseSwapPossible = isReverseSwapPossible(), ) @@ -390,9 +390,7 @@ internal class SwapViewModel @Inject constructor( } private fun selectProvider(state: Map): SwapProvider { - val consideredProviders = state.filter { - it.value is SwapState.QuotesLoadedState || isExchangeTooSmallAmountError(it.value) - } + val consideredProviders = state.consideredProvidersStates() return if (consideredProviders.isNotEmpty()) { val currentSelected = dataState.selectedProvider @@ -948,6 +946,12 @@ internal class SwapViewModel @Inject constructor( .mapValues { it.value as SwapState.QuotesLoadedState } } + private fun Map.consideredProvidersStates(): Map { + return this.filter { + it.value is SwapState.QuotesLoadedState || isExchangeTooSmallAmountError(it.value) + } + } + private fun isReverseSwapPossible(): Boolean { val from = dataState.fromCryptoCurrency ?: return false val to = dataState.toCryptoCurrency ?: return false From c170dd95d5c3da4bfcedefa7aa4eab88a64f6f7a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Dec 2023 17:09:56 +0200 Subject: [PATCH 05/17] Updated on 2026-08-14 --- .../tangem/feature/swap/ui/StateBuilder.kt | 8 ++------ .../feature/swap/viewmodels/SwapViewModel.kt | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) 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 dc89739051..5b22001266 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 @@ -98,12 +98,8 @@ internal class StateBuilder( if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder return uiStateHolder.copy( sendCardData = SwapCardState.SwapCardData( - type = TransactionCardType.ReadOnly( - headerResId = R.string.exchange_send_view_header, - ), - amountTextFieldValue = TextFieldValue( - text = "0", - ), + type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable), + amountTextFieldValue = null, amountEquivalent = "0 ${appCurrencyProvider.invoke().symbol}", token = fromToken, tokenIconUrl = uiStateHolder.sendCardData.tokenIconUrl, 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 8dc1e1a382..ccccda01cc 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 @@ -692,19 +692,22 @@ internal class SwapViewModel @Inject constructor( private fun onAmountChanged(value: String) { val fromToken = dataState.fromCryptoCurrency val toToken = dataState.toCryptoCurrency - if (fromToken != null && toToken != null) { + if (fromToken != null) { val decimals = fromToken.currency.decimals val cutValue = cutAmountWithDecimals(decimals, value) lastAmount.value = cutValue uiState = stateBuilder.updateSwapAmount(uiState, inputNumberFormatter.formatWithThousands(cutValue, decimals)) - amountDebouncer.debounce(viewModelScope, DEBOUNCE_AMOUNT_DELAY) { - startLoadingQuotes( - fromToken = fromToken, - toToken = toToken, - amount = lastAmount.value, - toProvidersList = findSwapProviders(fromToken, toToken), - ) + + if (toToken != null) { + amountDebouncer.debounce(viewModelScope, DEBOUNCE_AMOUNT_DELAY) { + startLoadingQuotes( + fromToken = fromToken, + toToken = toToken, + amount = lastAmount.value, + toProvidersList = findSwapProviders(fromToken, toToken), + ) + } } } } From 90e5684d027f9742e340dc7e909817fd645586ab Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Dec 2023 18:24:21 +0200 Subject: [PATCH 06/17] Updated on 2026-08-14 --- .../com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt index 0621d0a12a..c77ff9d577 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt @@ -79,7 +79,11 @@ private fun ChooseProviderBottomSheetContent(content: ChooseProviderBottomSheetC enabled = provider.onProviderClick != null, onClick = { provider.onProviderClick?.invoke(provider.id) }, ) - .padding(TangemTheme.dimens.spacing12), + .padding( + top = TangemTheme.dimens.spacing12, + bottom = TangemTheme.dimens.spacing12, + end = TangemTheme.dimens.spacing12, + ), ) } } From 7e5473b8c562a37c6bd201a62a9a5b8249048a9b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Dec 2023 19:30:33 +0300 Subject: [PATCH 07/17] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 3 +++ core/res/src/main/res/values/strings.xml | 3 ++- .../ui/components/exchange/ExchangeStatusBottomSheet.kt | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index c2cdf27727..c2434991f2 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -732,4 +732,7 @@ Сканировать карту Используйте %s или отсканируйте карту для входа в приложение C возвращением! + Обмен через %s + Данные провайдера. Сумма к получению может измениться в зависимости от рыночных условий. + Статус обмена diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 94f6ed4c2e..7998107847 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -205,6 +205,7 @@ Operation failed by provider Visit provider’s website for verification KYC verification required by provider + Cancelled Confirmed Confirming Confirming… @@ -219,7 +220,7 @@ Sending to you Sending to you… Sent - Provider-sourced data. Estimated amount subject to change. + Provider-sourced data. Estimated amount subject to change due to market conditions. Exchange status Verified Verification required diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt index 7cd58e238d..ea31904d30 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import com.tangem.core.ui.R import com.tangem.core.ui.components.SpacerH10 import com.tangem.core.ui.components.SpacerH12 @@ -50,6 +51,7 @@ private fun ExchangeStatusBottomSheetContent(content: ExchangeStatusBottomSheetC text = stringResource(id = R.string.express_exchange_status_subtitle), style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.secondary, + textAlign = TextAlign.Center, modifier = Modifier .align(CenterHorizontally), ) From 9ae8e54953acbe3bbd9884646b6b3a76fbe2fa3d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 25 Dec 2023 19:30:50 +0300 Subject: [PATCH 08/17] Updated on 2026-08-14 --- .../models/response/ExchangeStatusResponse.kt | 3 + core/res/src/main/res/values-ru/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 2 +- .../domain/models/domain/ExchangeStatus.kt | 1 + .../state/SwapTransactionsState.kt | 1 + ...enDetailsSwapTransactionsStateConverter.kt | 58 +++++++++++++------ .../exchange/ExchangeStatusBlock.kt | 20 +++++-- .../exchange/ExchangeStatusBottomSheet.kt | 2 +- .../exchange/ExchangeStatusItems.kt | 8 ++- .../viewmodels/TokenDetailsViewModel.kt | 3 +- 10 files changed, 70 insertions(+), 29 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt index 94cd9cbf1f..3be2a6ebec 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeStatusResponse.kt @@ -48,6 +48,9 @@ enum class ExchangeStatus { @Json(name = "verifying") VERIFYING, + + @Json(name = "cancelled") + CANCELLED, } data class ExchangeStatusError( diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index c2434991f2..afc6fda219 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -205,6 +205,7 @@ Операция не выполнена провайдером Посетите сайт провайдера для проверки Провайдер запрашивает прохождение верификации + Отменен Подтверждено Подтверждение Подтверждение... diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 7998107847..6548a846ee 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -205,7 +205,7 @@ Operation failed by provider Visit provider’s website for verification KYC verification required by provider - Cancelled + Canceled Confirmed Confirming Confirming… diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt index e412c74751..1e3727b208 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeStatus.kt @@ -17,4 +17,5 @@ enum class ExchangeStatus { Sending, Finished, Refunded, + Cancelled, } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt index d55821009b..c1e0f67210 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/SwapTransactionsState.kt @@ -29,6 +29,7 @@ internal data class SwapTransactionsState( val fromCryptoSymbol: String, val fromFiatAmount: String, val fromCurrencyIcon: TokenIconState, + val showProviderLink: Boolean, val onClick: () -> Unit, val onGoToProviderClick: (String) -> Unit, ) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt index 514afc7709..c5069303c7 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt @@ -62,6 +62,8 @@ internal class TokenDetailsSwapTransactionsStateConverter( val toFiatAmount = toAmount.multiply(toCurrency.value.fiatRate) val fromFiatAmount = fromAmount.multiply(fromCurrency.value.fiatRate) val timestamp = transaction.timestamp + val notifications = getNotification(transaction.status?.status, transaction.status?.txExternalUrl) + val showProviderLink = getShowProviderLink(notifications, transaction.status) result.add( SwapTransactionsState( txId = transaction.txId, @@ -92,6 +94,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( fromCryptoSymbol = fromCurrency.currency.symbol, fromFiatAmount = getFiatAmount(fromFiatAmount), fromCurrencyIcon = iconStateConverter.convert(fromCurrency), + showProviderLink = showProviderLink, onClick = { clickIntents.onSwapTransactionClick(transaction.txId) }, onGoToProviderClick = { url -> analyticsEventsHandlerProvider().send( @@ -109,12 +112,15 @@ internal class TokenDetailsSwapTransactionsStateConverter( fun updateTxStatus(tx: SwapTransactionsState, statusModel: ExchangeStatusModel?): SwapTransactionsState { if (statusModel == null || tx.activeStatus == statusModel.status) return tx val hasFailed = tx.hasFailed || statusModel.status == ExchangeStatus.Failed + val notifications = getNotification(statusModel.status, statusModel.txExternalUrl) + val showProviderLink = getShowProviderLink(notifications, statusModel) return tx.copy( activeStatus = statusModel.status, hasFailed = hasFailed, - notification = getNotification(statusModel.status, statusModel.txExternalUrl), + notification = notifications, statuses = getStatuses(statusModel.status, hasFailed), txUrl = statusModel.txExternalUrl, + showProviderLink = showProviderLink, ) } @@ -149,6 +155,9 @@ internal class TokenDetailsSwapTransactionsStateConverter( } } + private fun getShowProviderLink(notifications: ExchangeStatusNotifications?, status: ExchangeStatusModel?) = + notifications == null && status?.txExternalUrl != null && status.status != ExchangeStatus.Cancelled + private fun getStatuses(status: ExchangeStatus?, hasFailed: Boolean = false): ImmutableList { if (status == null) return persistentListOf() val isWaiting = status == ExchangeStatus.New || status == ExchangeStatus.Waiting @@ -164,26 +173,37 @@ internal class TokenDetailsSwapTransactionsStateConverter( val isExchangingDone = !isExchanging && isConfirmingDone val isSendingDone = !isSending && !isVerifying && !isFailed && isExchangingDone - return listOf( - waitStep(isWaiting, isWaitingDone), - confirmStep(isConfirming, isConfirmingDone), - exchangeStep( - isExchanging = isExchanging, - isExchangingDone = isExchangingDone, - isRefunded = isRefunded, - hasFailed = hasFailed, - isVerifying = isVerifying, - isFailed = isFailed, - ), - sendStep( - isSending = isSending, - isSendingDone = isSendingDone, - isRefunded = isRefunded, - hasFailed = hasFailed, - ), - ).toPersistentList() + return if (status == ExchangeStatus.Cancelled) { + listOf(cancelledStep()) + } else { + listOf( + waitStep(isWaiting, isWaitingDone), + confirmStep(isConfirming, isConfirmingDone), + exchangeStep( + isExchanging = isExchanging, + isExchangingDone = isExchangingDone, + isRefunded = isRefunded, + hasFailed = hasFailed, + isVerifying = isVerifying, + isFailed = isFailed, + ), + sendStep( + isSending = isSending, + isSendingDone = isSendingDone, + isRefunded = isRefunded, + hasFailed = hasFailed, + ), + ) + }.toPersistentList() } + private fun cancelledStep() = ExchangeStatusState( + status = ExchangeStatus.Cancelled, + text = TextReference.Res(R.string.express_exchange_status_canceled), + isActive = true, + isDone = true, + ) + private fun waitStep(isNew: Boolean, isNewDone: Boolean) = ExchangeStatusState( status = ExchangeStatus.New, text = when { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt index 1574bb2db6..ac5edc4fc4 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBlock.kt @@ -75,11 +75,15 @@ internal fun ExchangeStatusBlock( } } - statuses.forEachIndexed { index, item -> - ExchangeStatusStep( - stepStatus = item, - isLast = index == statuses.lastIndex, - ) + AnimatedContent(targetState = statuses.lastIndex, label = "Exchange Status List Change") { + Column { + statuses.forEachIndexed { index, item -> + ExchangeStatusStep( + stepStatus = item, + isLast = index == it, + ) + } + } } } } @@ -101,6 +105,11 @@ private fun ExchangeStatusStep( .size(TangemTheme.dimens.size20), ) { when { + it.status == ExchangeStatus.Cancelled -> ExchangeStep( + iconRes = R.drawable.ic_close_24, + color = TangemTheme.colors.icon.warning, + isDone = false, + ) it.status == ExchangeStatus.Failed -> ExchangeStep( iconRes = R.drawable.ic_close_24, color = TangemTheme.colors.icon.warning, @@ -131,6 +140,7 @@ private fun ExchangeStatusStep( @Composable private fun ExchangeStatusStepText(stepStatus: ExchangeStatusState) { val textColor = when { + stepStatus.status == ExchangeStatus.Cancelled -> TangemTheme.colors.icon.warning stepStatus.status == ExchangeStatus.Failed && !stepStatus.isDone -> TangemTheme.colors.icon.warning stepStatus.status == ExchangeStatus.Verifying && !stepStatus.isDone -> TangemTheme.colors.icon.attention stepStatus.isDone -> TangemTheme.colors.text.primary1 diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt index ea31904d30..da9113487a 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusBottomSheet.kt @@ -76,7 +76,7 @@ private fun ExchangeStatusBottomSheetContent(content: ExchangeStatusBottomSheetC SpacerH12() ExchangeStatusBlock( statuses = config.statuses, - showLink = config.notification == null && config.txUrl != null, + showLink = config.showProviderLink, onClick = { config.onGoToProviderClick(config.txUrl.orEmpty()) }, ) AnimatedContent( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt index c32011f436..94c1d12187 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/exchange/ExchangeStatusItems.kt @@ -4,7 +4,9 @@ import androidx.annotation.DrawableRes import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -42,7 +44,9 @@ internal fun LazyListScope.swapTransactionsItems( val item = swapTxs[it] val (iconRes, tint) = when (item.activeStatus) { ExchangeStatus.Verifying -> R.drawable.ic_alert_triangle_20 to TangemTheme.colors.icon.attention - ExchangeStatus.Failed -> R.drawable.ic_alert_circle_24 to TangemTheme.colors.icon.warning + ExchangeStatus.Failed, ExchangeStatus.Cancelled -> { + R.drawable.ic_alert_circle_24 to TangemTheme.colors.icon.warning + } else -> null to null } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 1c3f77a105..fee7ee1a64 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -125,6 +125,7 @@ internal class TokenDetailsViewModel @Inject constructor( clickIntents = this, appCurrencyProvider = Provider { selectedAppCurrencyFlow.value }, analyticsEventsHandlerProvider = Provider { analyticsEventsHandler }, + currentStateProvider = Provider { uiState }, userWalletId = userWalletId, cryptoCurrency = cryptoCurrency, ) @@ -229,7 +230,7 @@ internal class TokenDetailsViewModel @Inject constructor( delay = EXCHANGE_STATUS_UPDATE_DELAY, task = { runCatching(dispatchers.io) { - exchangeStatusFactory.updateSwapTxStatuses(swapTxs) + exchangeStatusFactory.updateSwapTxStatuses(uiState.swapTxs) } }, onSuccess = ::updateSwapTx, From 2e886954d89fcc3e32d793c990c2265934e385e6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 26 Dec 2023 09:38:33 +0200 Subject: [PATCH 09/17] Updated on 2026-08-14 --- .../java/com/tangem/feature/swap/ui/SwapScreenContent.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 571a1b51f1..f0d49bdd3c 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 @@ -131,7 +131,7 @@ private fun MainInfo(state: SwapStateHolder) { }, onSelectTokenClick = state.onSelectTokenClick, ) - val marginCard = TangemTheme.dimens.spacing16 + val marginCard = TangemTheme.dimens.spacing12 TransactionCardData( priceImpact = priceImpact, swapCardState = state.receiveCardData, @@ -140,7 +140,7 @@ private fun MainInfo(state: SwapStateHolder) { }, onSelectTokenClick = state.onSelectTokenClick, ) - val marginButton = TangemTheme.dimens.spacing32 + val marginButton = TangemTheme.dimens.spacing30 SwapButton( state, modifier = Modifier.constrainAs(button) { @@ -494,12 +494,12 @@ private val state = SwapStateHolder( priceImpact = PriceImpact.Empty(), tosState = TosState( tosLink = LegalState( - title = stringReference("Tangem"), + title = stringReference("Terms of Use"), link = "https://tangem.com", onClick = {}, ), policyLink = LegalState( - title = stringReference("Tangem"), + title = stringReference("Privacy Policy"), link = "https://tangem.com", onClick = {}, ), From a5ac876d006a7176961e5e403747896de867cf1f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 26 Dec 2023 13:01:29 +0200 Subject: [PATCH 10/17] Updated on 2026-08-14 --- .../core/ui/components/appbar/AppBarWithSearch.kt | 1 - .../feature/swap/ui/ChooseProviderBottomSheet.kt | 11 +++-------- .../tangem/feature/swap/ui/SwapSelectTokenScreen.kt | 2 -- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt index 867774adfb..4dfd01d36d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt @@ -95,7 +95,6 @@ private fun CollapsedSearchView( modifier = Modifier .background(TangemTheme.colors.background.secondary) .fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), verticalAlignment = Alignment.CenterVertically, ) { Icon( diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt index c77ff9d577..07091dfb12 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt @@ -36,14 +36,13 @@ fun ChooseProviderBottomSheet(config: TangemBottomSheetConfig) { @Composable private fun ChooseProviderBottomSheetContent(content: ChooseProviderBottomSheetConfig) { - Column { + Column(horizontalAlignment = Alignment.CenterHorizontally) { Text( text = stringResource(R.string.express_choose_providers_title), style = TangemTheme.typography.subtitle1, color = TangemTheme.colors.text.primary1, modifier = Modifier .padding(top = TangemTheme.dimens.spacing10) - .align(Alignment.CenterHorizontally), ) Text( text = stringResource(R.string.express_choose_providers_subtitle), @@ -51,8 +50,7 @@ private fun ChooseProviderBottomSheetContent(content: ChooseProviderBottomSheetC color = TangemTheme.colors.text.secondary, modifier = Modifier .padding(top = TangemTheme.dimens.spacing10) - .padding(horizontal = TangemTheme.dimens.spacing56) - .align(Alignment.CenterHorizontally), + .padding(horizontal = TangemTheme.dimens.spacing56), textAlign = TextAlign.Center, ) Column( @@ -91,8 +89,6 @@ private fun ChooseProviderBottomSheetContent(content: ChooseProviderBottomSheetC painterResource(id = R.drawable.ic_lightning_16), contentDescription = null, tint = TangemTheme.colors.icon.informative, - modifier = Modifier - .align(Alignment.CenterHorizontally), ) Text( text = stringResource(R.string.express_more_providers_soon), @@ -100,8 +96,7 @@ private fun ChooseProviderBottomSheetContent(content: ChooseProviderBottomSheetC color = TangemTheme.colors.icon.informative, modifier = Modifier .padding(top = TangemTheme.dimens.spacing6, bottom = TangemTheme.dimens.spacing16) - .padding(horizontal = TangemTheme.dimens.spacing56) - .align(Alignment.CenterHorizontally), + .padding(horizontal = TangemTheme.dimens.spacing56), textAlign = TextAlign.Center, ) } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt index 87a82413f1..ed54834868 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt @@ -127,8 +127,6 @@ private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier = .imePadding(), horizontalAlignment = Alignment.CenterHorizontally, ) { - item { SpacerH8() } - tokensToSelectItems(state.availableTokens, state.onTokenSelected) item { SpacerH12() } From f2ad0bd917aabdd4b9fb97f44e8912190c058420 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 26 Dec 2023 14:16:04 +0300 Subject: [PATCH 11/17] Updated on 2026-08-14 --- .../settings/DefaultSwapPromoRepository.kt | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt index dd94a2e07c..8ba9fe211e 100644 --- a/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt +++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt @@ -40,16 +40,23 @@ class DefaultSwapPromoRepository( ) } - private fun checkPromoPeriod(): Boolean { - val currentDate = Calendar.getInstance() - return currentDate.get(Calendar.DATE) in START_DATE_KEY..END_DATE_KEY && - currentDate.get(Calendar.MONTH) == MONTH_KEY && currentDate.get(Calendar.YEAR) == YEAR_KEY + private suspend fun checkPromoPeriod(): Boolean { + val calendar = Calendar.getInstance() + val currentTime = calendar.timeInMillis + calendar.set(END_YEAR_KEY, END_MONTH_KEY, END_DAY_KEY, 0, 0, 0) + val endTime = calendar.timeInMillis + + val shouldShow = endTime - currentTime > 0 + if (!shouldShow) { + setNeverToShowToken() + setNeverToShowWallet() + } + return shouldShow } companion object { - private const val START_DATE_KEY = 15 - private const val END_DATE_KEY = 31 - private const val MONTH_KEY = 11 // December - private const val YEAR_KEY = 2023 // just in case + private const val END_DAY_KEY = 1 + private const val END_MONTH_KEY = 1 // February + private const val END_YEAR_KEY = 2024 } } \ No newline at end of file From 1995610c6a778f219dcfd9c12061e3663e9bc3e1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 26 Dec 2023 15:10:32 +0300 Subject: [PATCH 12/17] Updated on 2026-08-14 --- .../swap/ui/ChooseProviderBottomSheet.kt | 2 +- .../viewmodels/ExchangeStatusFactory.kt | 53 ++++++++++++------- .../viewmodels/TokenDetailsViewModel.kt | 5 ++ 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt index 07091dfb12..36c705d5fb 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt @@ -42,7 +42,7 @@ private fun ChooseProviderBottomSheetContent(content: ChooseProviderBottomSheetC style = TangemTheme.typography.subtitle1, color = TangemTheme.colors.text.primary1, modifier = Modifier - .padding(top = TangemTheme.dimens.spacing10) + .padding(top = TangemTheme.dimens.spacing10), ) Text( text = stringResource(R.string.express_choose_providers_subtitle), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt index 4c78d11155..8ff627eaec 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt @@ -18,7 +18,9 @@ import com.tangem.feature.swap.domain.models.domain.ExchangeStatus import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsSwapTransactionsStateConverter +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheetConfig import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf @@ -41,6 +43,7 @@ internal class ExchangeStatusFactory( private val clickIntents: TokenDetailsClickIntents, private val appCurrencyProvider: Provider, private val analyticsEventsHandlerProvider: Provider, + private val currentStateProvider: Provider, private val userWalletId: UserWalletId, private val cryptoCurrency: CryptoCurrency, ) { @@ -64,6 +67,27 @@ internal class ExchangeStatusFactory( ) } + suspend fun removeTransactionOnBottomSheetClosed(): TokenDetailsState { + val state = currentStateProvider() + val bottomSheetConfig = state.bottomSheetConfig?.content as? ExchangeStatusBottomSheetConfig ?: return state + val selectedTx = bottomSheetConfig.value + + return if (selectedTx.activeStatus.isTerminal()) { + swapTransactionRepository.removeTransaction( + userWalletId = userWalletId, + fromCryptoCurrencyId = selectedTx.fromCryptoCurrencyId, + toCryptoCurrencyId = selectedTx.toCryptoCurrencyId, + txId = selectedTx.txId, + ) + val filteredTxs = state.swapTxs + .filterNot { it.txId == selectedTx.txId } + .toPersistentList() + state.copy(swapTxs = filteredTxs) + } else { + state + } + } + private fun getWalletCryptoCurrencies() = flow { val selectedWallet = getSelectedWalletSyncUseCase().fold( ifLeft = { null }, @@ -80,14 +104,16 @@ internal class ExchangeStatusFactory( suspend fun updateSwapTxStatuses(swapTxList: PersistentList) = withContext(dispatchers.io) { swapTxList.map { tx -> async { - val statusModel = getExchangeStatus(tx.txId) - swapTransactionsStateConverter - .updateTxStatus(tx, statusModel) - .removeIfFinished(statusModel?.status) + if (tx.activeStatus.isTerminal()) { + tx + } else { + val statusModel = getExchangeStatus(tx.txId) + swapTransactionsStateConverter + .updateTxStatus(tx, statusModel) + } } } .awaitAll() - .filterNotNull() .toPersistentList() } @@ -130,21 +156,8 @@ internal class ExchangeStatusFactory( ) } - private suspend fun SwapTransactionsState.removeIfFinished(status: ExchangeStatus?) = when (status) { - null -> null // not found - ExchangeStatus.Refunded, ExchangeStatus.Finished -> { - swapTransactionRepository.removeTransaction( - userWalletId = userWalletId, - fromCryptoCurrencyId = fromCryptoCurrencyId, - toCryptoCurrencyId = toCryptoCurrencyId, - txId = txId, - ) - null - } - else -> { - this - } - } + private fun ExchangeStatus?.isTerminal() = + this == ExchangeStatus.Refunded || this == ExchangeStatus.Finished || this == ExchangeStatus.Cancelled private fun toAnalyticStatus(status: ExchangeStatus?): ExchangeAnalyticsStatus? { return when (status) { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index fee7ee1a64..b2d4ba321e 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -548,6 +548,11 @@ internal class TokenDetailsViewModel @Inject constructor( } override fun onDismissBottomSheet() { + if (uiState.bottomSheetConfig?.content is ExchangeStatusBottomSheetConfig) { + viewModelScope.launch(dispatchers.main) { + uiState = exchangeStatusFactory.removeTransactionOnBottomSheetClosed() + } + } uiState = stateFactory.getStateWithClosedBottomSheet() } From c7aabfa5d32166643d411bdd954ecfec5a0c86a4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 26 Dec 2023 16:07:33 +0400 Subject: [PATCH 13/17] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 237bd3e4a9..805056d1a5 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -88,7 +88,7 @@ spr-client = "3.6.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.4-423" +tangemBlockchainSdk = "release-app_5.4-434" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.4-315" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From c626a0c35f673539ec3dd6d8d9abc421826ed836 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 26 Dec 2023 15:28:35 +0300 Subject: [PATCH 14/17] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 6 ++--- .../src/main/res/values-zh-rTW/strings.xml | 2 -- core/res/src/main/res/values/strings.xml | 6 ++--- .../core/ui/utils/BigDecimalFormatter.kt | 8 +++++- .../swap/converters/TokensDataConverter.kt | 18 ++++++++----- .../models/CurrenciesGroupWithFromCurrency.kt | 9 +++++++ .../feature/swap/models/SwapStateHolder.kt | 4 +-- .../swap/ui/ChooseProviderBottomSheet.kt | 1 + .../tangem/feature/swap/ui/StateBuilder.kt | 26 ++++++++++++------- .../feature/swap/ui/SwapScreenContent.kt | 2 +- .../feature/swap/ui/SwapSelectTokenScreen.kt | 1 + .../feature/swap/ui/SwapSuccessScreen.kt | 4 +-- .../feature/swap/viewmodels/SwapViewModel.kt | 5 ++-- .../components/TokenDetailsActionButton.kt | 2 +- .../viewmodels/TokenDetailsViewModel.kt | 1 - .../state/components/WalletManageButton.kt | 2 +- .../state/factory/TokenActionsProvider.kt | 2 +- 17 files changed, 61 insertions(+), 38 deletions(-) create mode 100644 features/swap/presentation/src/main/java/com/tangem/feature/swap/models/CurrenciesGroupWithFromCurrency.kt diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index afc6fda219..f9f34ddd5f 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -189,8 +189,6 @@ Условия использования К сожалению, текущая версия приложения не готова к работе с этой картой, проверьте наличие обновлений Вы использовали карту от другого кошелька. Приложите карту, связанную с этим кошельком. - Вы получаете - Вы отправляете Мои токены У вас нет добавленных токенов. Добавьте токены для обмена Токены не найдены. Пожалуйста, попробуйте другой запрос @@ -524,6 +522,7 @@ Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен. Подтвердить Ошибка: %s + Вы отправляете Произошла ошибка. Пожалуйста, попробуйте еще раз. Дать разрешение Сильные колебания цены! @@ -542,13 +541,12 @@ Ваш кошелек Чтобы продолжить, вам нужно разрешить смарт-контракту 1inch использовать ваш %s Безлимитно - Вы отправляете - Вы получите Открыть в обозревателе В процессе Обменять Обмен %s на Котировки включают дополнительную комиссию Tangem в размере %s. Это помогает нам предоставлять первоклассный продукт. + Вы получите Другие токены Выберите токен Ваши токены diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index f595e68773..adb5903417 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -137,8 +137,6 @@ 服務條款 糟糕,當前版本的應用程序無法使用此卡,請檢查更新 您使用了另一個錢包中的卡。點按與此錢包關聯的卡片 - 您收到 - 您發送 以下信息是可選的。如果不想共享,可以將其刪除 告訴我們您缺少哪些功能,我們會盡力幫助您 請告訴我們你有什麼卡 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 6548a846ee..bf64538d4b 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -188,8 +188,6 @@ Terms of Service Oops, the current version of the application is not ready to work with this card, please check for updates. You have used a card from another wallet. Tap the card associated with this wallet - You receive - You send My tokens You haven\'t added any tokens yet. Add tokens via Market to swap No tokens found. Please try another request @@ -535,6 +533,7 @@ All decentralized exchanges require approvals to prevent smart contracts from accessing your wallet without your permission. By design, smart contracts can\'t access your tokens unless you approve. By \"unlocking\" your tokens, you authorize the 1-inch smart contract to spend them. The network\'s miners receive a gas fee (paid by you) to record this action on the blockchain. You can swap your token after giving approval. Approve Error: %s + You swap There was an error. Please try again. Give Permission High price impact! @@ -553,13 +552,12 @@ Your wallet To continue, grant 1inch smart contracts permission to use your %s Unlimited - You swap - You receive View in Explorer In progress Swap Swap %s for Quotes include an additional Tangem commission of %s. This helps us deliver a top-of-the-line product. + You receive Other tokens Choose token Your tokens diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt index c64c860e28..e2d0c8dc42 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt @@ -22,7 +22,13 @@ object BigDecimalFormatter { roundingMode = RoundingMode.DOWN } - return formatter.format(cryptoAmount) + "\u2009$cryptoCurrency" + return formatter.format(cryptoAmount).let { + if (cryptoCurrency.isEmpty()) { + it + } else { + it + "\u2009$cryptoCurrency" + } + } } fun formatCryptoAmount(cryptoAmount: BigDecimal?, cryptoCurrency: CryptoCurrency): String { 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 d475bad6f2..936254d3fa 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 @@ -8,7 +8,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.swap.domain.models.domain.CryptoCurrencySwapInfo -import com.tangem.feature.swap.domain.models.ui.CurrenciesGroup +import com.tangem.feature.swap.models.CurrenciesGroupWithFromCurrency import com.tangem.feature.swap.models.SwapSelectTokenStateHolder import com.tangem.feature.swap.models.TokenBalanceData import com.tangem.feature.swap.models.TokenToSelectState @@ -21,17 +21,21 @@ class TokensDataConverter( private val onTokenSelected: (String) -> Unit, private val isBalanceHiddenProvider: Provider, private val appCurrencyProvider: Provider, -) : Converter { +) : Converter { - override fun convert(value: CurrenciesGroup): SwapSelectTokenStateHolder { + override fun convert(value: CurrenciesGroupWithFromCurrency): SwapSelectTokenStateHolder { + val group = value.group val availableTitle = TokenToSelectState.Title( resourceReference(R.string.exchange_tokens_available_tokens_header), ) val unavailableTitle = TokenToSelectState.Title( - resourceReference(R.string.exchange_tokens_available_tokens_header), + resourceReference( + R.string.exchange_tokens_unavailable_tokens_header, + wrappedList(value.fromCurrency.name), + ), ) return SwapSelectTokenStateHolder( - availableTokens = value.available.map { tokenWithBalanceToTokenToSelect(it, true) } + availableTokens = group.available.map { tokenWithBalanceToTokenToSelect(it, true) } .toMutableList() .apply { if (this.isNotEmpty()) { @@ -39,7 +43,7 @@ class TokensDataConverter( } } .toImmutableList(), - unavailableTokens = value.unavailable.map { tokenWithBalanceToTokenToSelect(it, false) } + unavailableTokens = group.unavailable.map { tokenWithBalanceToTokenToSelect(it, false) } .toMutableList() .apply { if (this.isNotEmpty()) { @@ -49,7 +53,7 @@ class TokensDataConverter( .toImmutableList(), onSearchEntered = onSearchEntered, onTokenSelected = onTokenSelected, - afterSearch = value.afterSearch, + afterSearch = group.afterSearch, ) } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/CurrenciesGroupWithFromCurrency.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/CurrenciesGroupWithFromCurrency.kt new file mode 100644 index 0000000000..8251580bb8 --- /dev/null +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/CurrenciesGroupWithFromCurrency.kt @@ -0,0 +1,9 @@ +package com.tangem.feature.swap.models + +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.feature.swap.domain.models.ui.CurrenciesGroup + +data class CurrenciesGroupWithFromCurrency( + val group: CurrenciesGroup, + val fromCurrency: CryptoCurrency, +) \ No newline at end of file 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 577c2e96af..2ccc7c9571 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 @@ -105,12 +105,12 @@ sealed interface TransactionCardType { data class Inputtable( val onAmountChanged: ((String) -> Unit), val onFocusChanged: ((Boolean) -> Unit), - @StringRes override val headerResId: Int = R.string.exchange_send_view_header, + @StringRes override val headerResId: Int = R.string.swapping_from_title, ) : TransactionCardType data class ReadOnly( val highPriceImpact: String? = null, - @StringRes override val headerResId: Int = R.string.exchange_receive_view_header, + @StringRes override val headerResId: Int = R.string.swapping_to_title, ) : TransactionCardType } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt index 36c705d5fb..d30d7591f2 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseProviderBottomSheet.kt @@ -34,6 +34,7 @@ fun ChooseProviderBottomSheet(config: TangemBottomSheetConfig) { } } +@Suppress("LongMethod") @Composable private fun ChooseProviderBottomSheetContent(content: ChooseProviderBottomSheetConfig) { Column(horizontalAlignment = Alignment.CenterHorizontally) { 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 5b22001266..5246e8ed36 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 @@ -107,7 +107,7 @@ internal class StateBuilder( isNotNativeToken = uiStateHolder.sendCardData.isNotNativeToken, tokenCurrency = uiStateHolder.sendCardData.tokenCurrency, canSelectAnotherToken = uiStateHolder.sendCardData.canSelectAnotherToken, - balance = fromToken.getFormattedAmount(), + balance = fromToken.getFormattedAmount(isNeedSymbol = false), networkIconRes = getActiveIconRes(fromToken.currency.network.id.value), isBalanceHidden = isBalanceHiddenProvider(), ), @@ -227,7 +227,7 @@ internal class StateBuilder( tokenCurrency = uiStateHolder.sendCardData.tokenCurrency, canSelectAnotherToken = uiStateHolder.sendCardData.canSelectAnotherToken, networkIconRes = uiStateHolder.sendCardData.networkIconRes, - balance = fromCurrencyStatus.getFormattedAmount(), + balance = fromCurrencyStatus.getFormattedAmount(isNeedSymbol = false), isBalanceHidden = isBalanceHiddenProvider(), ), receiveCardData = SwapCardState.SwapCardData( @@ -241,7 +241,7 @@ internal class StateBuilder( tokenCurrency = uiStateHolder.receiveCardData.tokenCurrency, canSelectAnotherToken = uiStateHolder.receiveCardData.canSelectAnotherToken, networkIconRes = uiStateHolder.receiveCardData.networkIconRes, - balance = toCurrencyStatus.getFormattedAmount(), + balance = toCurrencyStatus.getFormattedAmount(isNeedSymbol = false), isBalanceHidden = isBalanceHiddenProvider(), ), networkCurrency = quoteModel.networkCurrency, @@ -437,7 +437,7 @@ internal class StateBuilder( tokenCurrency = uiStateHolder.receiveCardData.tokenCurrency, canSelectAnotherToken = uiStateHolder.receiveCardData.canSelectAnotherToken, networkIconRes = uiStateHolder.receiveCardData.networkIconRes, - balance = toToken.getFormattedAmount(), + balance = toToken.getFormattedAmount(isNeedSymbol = false), isBalanceHidden = isBalanceHiddenProvider(), ) } ?: SwapCardState.Empty( @@ -451,6 +451,7 @@ internal class StateBuilder( return uiStateHolder.copy( sendCardData = uiStateHolder.sendCardData.copy( amountEquivalent = getFormattedFiatAmount(fromToken.amountFiat), + balance = fromToken.cryptoCurrencyStatus.getFormattedAmount(isNeedSymbol = false), ), receiveCardData = receiveCardData, warnings = warnings, @@ -589,10 +590,17 @@ internal class StateBuilder( ) } - fun addTokensToState(uiState: SwapStateHolder, tokensDataState: CurrenciesGroup): SwapStateHolder { + fun addTokensToState( + uiState: SwapStateHolder, + fromToken: CryptoCurrency, + tokensDataState: CurrenciesGroup, + ): SwapStateHolder { return uiState.copy( selectTokenState = tokensDataConverter.convert( - value = tokensDataState, + value = CurrenciesGroupWithFromCurrency( + fromCurrency = fromToken, + group = tokensDataState, + ), ), ) } @@ -1220,10 +1228,10 @@ internal class StateBuilder( ) } - private fun CryptoCurrencyStatus.getFormattedAmount(): String { + private fun CryptoCurrencyStatus.getFormattedAmount(isNeedSymbol: Boolean): String { val amount = value.amount ?: return UNKNOWN_AMOUNT_SIGN - - return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals) + val symbol = if (isNeedSymbol) currency.symbol else "" + return BigDecimalFormatter.formatCryptoAmount(amount, symbol, currency.decimals) } @Suppress("UnusedPrivateMember") 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 f0d49bdd3c..e3c408bde2 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 @@ -413,7 +413,7 @@ private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> U else -> { PrimaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), - text = stringResource(id = R.string.common_swap), + text = stringResource(id = R.string.swapping_swap_action), iconResId = R.drawable.ic_tangem_24, enabled = state.swapButton.enabled, onClick = state.swapButton.onClick, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt index ed54834868..930039d697 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt @@ -180,6 +180,7 @@ private fun TitleHeader(item: TokenToSelectState.Title, modifier: Modifier = Mod Text( text = item.title.resolveReference().uppercase(), style = TangemTheme.typography.overline, + color = TangemTheme.colors.text.tertiary, modifier = Modifier .padding( top = TangemTheme.dimens.spacing16, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt index d540514a81..4b44fb9b18 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt @@ -60,7 +60,7 @@ private fun SwapSuccessScreenContent(state: SwapSuccessStateHolder, padding: Pad TransactionDoneTitle(titleRes = R.string.swapping_success_view_title, date = state.timestamp) SpacerH16() InputRowImage( - title = TextReference.Res(R.string.swapping_success_from_title), + title = TextReference.Res(R.string.swapping_from_title), subtitle = state.fromTokenAmount, caption = state.fromTokenFiatAmount, tokenIconState = state.fromTokenIconState ?: TokenIconState.Loading, @@ -70,7 +70,7 @@ private fun SwapSuccessScreenContent(state: SwapSuccessStateHolder, padding: Pad ) SpacerH16() InputRowImage( - title = TextReference.Res(R.string.swapping_success_to_title), + title = TextReference.Res(R.string.swapping_to_title), subtitle = state.toTokenAmount, caption = state.toTokenFiatAmount, tokenIconState = state.toTokenIconState ?: TokenIconState.Loading, 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 ccccda01cc..a18226212a 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 @@ -216,11 +216,12 @@ internal class SwapViewModel @Inject constructor( } } - private fun updateTokensState(dataState: TokensDataStateExpress) { - val tokensDataState = if (isOrderReversed) dataState.fromGroup else dataState.toGroup + private fun updateTokensState(tokenDataState: TokensDataStateExpress) { + val tokensDataState = if (isOrderReversed) tokenDataState.fromGroup else tokenDataState.toGroup uiState = stateBuilder.addTokensToState( uiState = uiState, tokensDataState = tokensDataState, + fromToken = dataState.fromCryptoCurrency?.currency ?: initialCryptoCurrency, ) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsActionButton.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsActionButton.kt index 8af1d13a13..3575d02e0f 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsActionButton.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsActionButton.kt @@ -78,7 +78,7 @@ internal sealed class TokenDetailsActionButton(val config: ActionButtonConfig) { */ data class Swap(val enabled: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton( config = ActionButtonConfig( - text = TextReference.Res(id = R.string.common_swap), + text = TextReference.Res(id = R.string.common_exchange), iconResId = R.drawable.ic_exchange_vertical_24, onClick = onClick, enabled = enabled, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index b2d4ba321e..68e840d39a 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -125,7 +125,6 @@ internal class TokenDetailsViewModel @Inject constructor( clickIntents = this, appCurrencyProvider = Provider { selectedAppCurrencyFlow.value }, analyticsEventsHandlerProvider = Provider { analyticsEventsHandler }, - currentStateProvider = Provider { uiState }, userWalletId = userWalletId, cryptoCurrency = cryptoCurrency, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt index fabf18401f..2628469375 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt @@ -88,7 +88,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) { */ data class Swap(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton( config = ActionButtonConfig( - text = TextReference.Res(id = R.string.common_swap), + text = TextReference.Res(id = R.string.common_exchange), iconResId = R.drawable.ic_exchange_vertical_24, onClick = onClick, enabled = enabled, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenActionsProvider.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenActionsProvider.kt index df989ef9b8..7a452a255c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenActionsProvider.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenActionsProvider.kt @@ -78,7 +78,7 @@ internal class TokenActionsProvider( action = { clickIntents.onMultiCurrencySendClick(cryptoCurrencyStatus) } } is TokenActionsState.ActionState.Swap -> { - title = resourceReference(R.string.common_swap) + title = resourceReference(R.string.common_exchange) icon = R.drawable.ic_exchange_horizontal_24 action = { clickIntents.onSwapClick(cryptoCurrencyStatus) } } From 41a7398497ae19b45ec5ab8326b13b8ca7c3a2dd Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 26 Dec 2023 15:53:58 +0300 Subject: [PATCH 15/17] Updated on 2026-08-14 --- .../tokendetails/viewmodels/TokenDetailsViewModel.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 68e840d39a..b2d4ba321e 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -125,6 +125,7 @@ internal class TokenDetailsViewModel @Inject constructor( clickIntents = this, appCurrencyProvider = Provider { selectedAppCurrencyFlow.value }, analyticsEventsHandlerProvider = Provider { analyticsEventsHandler }, + currentStateProvider = Provider { uiState }, userWalletId = userWalletId, cryptoCurrency = cryptoCurrency, ) From a7e98802e9833fba813193400e87dbf0124689ec Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 26 Dec 2023 16:20:10 +0300 Subject: [PATCH 16/17] Updated on 2026-08-14 --- .../core/ui/components/buttons/common/TangemButtonSize.kt | 2 +- .../presentation/common/component/NetworkGroupItem.kt | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonSize.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonSize.kt index 0370fe0efe..bfae5cb313 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonSize.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonSize.kt @@ -111,7 +111,7 @@ internal fun TangemButtonSize.toHorizontalContentPadding(icon: TangemButtonIconP TangemButtonSize.Default, TangemButtonSize.WideAction, TangemButtonSize.TwoLines, - -> TangemTheme.dimens.spacing32 to TangemTheme.dimens.spacing32 + -> TangemTheme.dimens.spacing16 to TangemTheme.dimens.spacing16 TangemButtonSize.Text -> when (icon) { is TangemButtonIconPosition.None -> TangemTheme.dimens.spacing16 to TangemTheme.dimens.spacing16 is TangemButtonIconPosition.Start -> TangemTheme.dimens.spacing14 to TangemTheme.dimens.spacing16 diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt index ae4d06b0dc..c5af5a2cf6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt @@ -19,10 +19,7 @@ import org.burnoutcrew.reorderable.detectReorder @Composable internal fun NetworkGroupItem(networkName: String, modifier: Modifier = Modifier) { - InternalNetworkGroupItem( - modifier = modifier, - networkName = networkName, - ) + InternalNetworkGroupItem(modifier = modifier, networkName = networkName) } @Composable @@ -69,12 +66,13 @@ private fun InternalNetworkGroupItem( .background(TangemTheme.colors.background.primary) .padding(horizontal = TangemTheme.dimens.spacing14) .fillMaxWidth() - .heightIn(min = TangemTheme.dimens.size48), + .heightIn(min = TangemTheme.dimens.size40), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { Text( text = stringResource(id = R.string.wallet_network_group_title, networkName), + modifier = Modifier.padding(top = TangemTheme.dimens.spacing12, bottom = TangemTheme.dimens.spacing8), style = TangemTheme.typography.subtitle2, color = TangemTheme.colors.text.tertiary, ) From 318d3edfa19e3bb6fae9f526eeae01f3104aaadc Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 26 Dec 2023 14:34:05 +0200 Subject: [PATCH 17/17] Updated on 2026-08-14 --- .../datasource/di/StatusCodeInterceptor.kt | 13 ++--- .../swap/converters/ErrorsDataConverter.kt | 52 +++++++++++++------ 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/StatusCodeInterceptor.kt b/core/datasource/src/main/java/com/tangem/datasource/di/StatusCodeInterceptor.kt index 7a6362efe3..4da4c8b17d 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/StatusCodeInterceptor.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/StatusCodeInterceptor.kt @@ -36,13 +36,14 @@ class StatusCodeInterceptor : Interceptor { } private fun getBody(): String { - return "\"error\": {\n" + - " \"code\": 2290,\n" + - " \"description\": \"Core: receivedDecimals is not equal to expressDecimals\",\n" + + return "{\n" + + " \"error\": {\n" + + " \"code\": 2250,\n" + + " \"description\": \"Core: exchange too small amount\",\n" + " \"message\": \"Not valid\",\n" + - " \"receivedToDecimals\": 5,\n" + - " \"expressToDecimals\": 5\n" + - " }" + " \"minAmount\": 5\n" + + " }\n" + + "}" } companion object { diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt index 6ca55d7f04..fa28cf44f2 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt @@ -1,6 +1,7 @@ package com.tangem.feature.swap.converters import com.squareup.moshi.JsonAdapter +import com.tangem.datasource.api.express.models.response.ExpressError import com.tangem.datasource.api.express.models.response.ExpressErrorResponse import com.tangem.feature.swap.domain.models.DataError import com.tangem.feature.swap.domain.models.createFromAmountWithOffset @@ -21,28 +22,47 @@ internal class ErrorsDataConverter( 2220 -> DataError.ExchangeProviderNotActiveError(code = error.code) 2230 -> DataError.ExchangeProviderNotAvailableError(code = error.code) 2240 -> DataError.ExchangeNotPossibleError(code = error.code) - 2250 -> DataError.ExchangeTooSmallAmountError( - code = error.code, - amount = createFromAmountWithOffset( - requireNotNull(error.value?.minAmount), - requireNotNull(error.value?.decimals), - ), - ) - 2260 -> DataError.ExchangeNotEnoughAllowanceError( - code = error.code, - currentAllowance = requireNotNull(error.value?.currentAllowance), - ) + 2250 -> tryParseExchangeTooSmallAmountError(error = error) + 2260 -> tryParseExchangeNotEnoughAllowanceError(error = error) 2270 -> DataError.ExchangeNotEnoughBalanceError(code = error.code) 2280 -> DataError.ExchangeInvalidAddressError(code = error.code) - 2290 -> DataError.ExchangeInvalidFromDecimalsError( - code = error.code, - receivedFromDecimals = requireNotNull(error.value?.receivedFromDecimals), - expressFromDecimals = requireNotNull(error.value?.expressFromDecimals), - ) + 2290 -> tryParseExchangeInvalidFromDecimalsError(error = error) else -> DataError.UnknownErrorWithCode(error.code) } } catch (e: Exception) { return DataError.UnknownError } } + + private fun tryParseExchangeTooSmallAmountError(error: ExpressError): DataError { + val minAmount = error.value?.minAmount ?: return DataError.UnknownErrorWithCode(error.code) + val decimals = error.value?.decimals ?: return DataError.UnknownErrorWithCode(error.code) + + return DataError.ExchangeTooSmallAmountError( + code = error.code, + amount = createFromAmountWithOffset(minAmount, decimals), + ) + } + + private fun tryParseExchangeNotEnoughAllowanceError(error: ExpressError): DataError { + val currentAllowance = error.value?.currentAllowance ?: return DataError.UnknownErrorWithCode(error.code) + + return DataError.ExchangeNotEnoughAllowanceError( + code = error.code, + currentAllowance = currentAllowance, + ) + } + + private fun tryParseExchangeInvalidFromDecimalsError(error: ExpressError): DataError { + val receivedFromDecimals = error.value?.receivedFromDecimals ?: return DataError.UnknownErrorWithCode( + code = error.code, + ) + val expressFromDecimals = error.value?.expressFromDecimals ?: return DataError.UnknownErrorWithCode(error.code) + + return DataError.ExchangeInvalidFromDecimalsError( + code = error.code, + receivedFromDecimals = receivedFromDecimals, + expressFromDecimals = expressFromDecimals, + ) + } } \ No newline at end of file