diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt
index b8e9b2e458..4f32eee854 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt
@@ -2,8 +2,8 @@ package com.tangem.tap.common.analytics.converters
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.common.core.TangemSdkError
+import com.tangem.domain.demo.DemoTransactionSender
import com.tangem.tap.common.analytics.events.AnalyticsParam
-import com.tangem.tap.features.demo.DemoTransactionSender
import com.tangem.utils.converter.Converter
/**
diff --git a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt
index 5fc760156f..bf4f00059e 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt
@@ -1,7 +1,7 @@
package com.tangem.tap.di.domain
import com.tangem.domain.card.repository.CardSdkConfigRepository
-import com.tangem.domain.demo.IsDemoCardUseCase
+import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.transaction.usecase.CreateTransactionUseCase
@@ -22,20 +22,24 @@ internal object TransactionDomainModule {
@Provides
@ViewModelScoped
fun provideGetFeeUseCase(walletManagersFacade: WalletManagersFacade): GetFeeUseCase {
- return GetFeeUseCase(walletManagersFacade)
+ return GetFeeUseCase(
+ walletManagersFacade = walletManagersFacade,
+ demoConfig = DemoConfig(),
+ )
}
@Provides
@ViewModelScoped
fun provideSendTransactionUseCase(
- isDemoCardUseCase: IsDemoCardUseCase,
cardSdkConfigRepository: CardSdkConfigRepository,
transactionRepository: TransactionRepository,
+ walletManagersFacade: WalletManagersFacade,
): SendTransactionUseCase {
return SendTransactionUseCase(
- isDemoCardUseCase = isDemoCardUseCase,
+ demoConfig = DemoConfig(),
cardSdkConfigRepository = cardSdkConfigRepository,
transactionRepository = transactionRepository,
+ walletManagersFacade = walletManagersFacade,
)
}
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt
index 2cb96aa486..cfdcf74a19 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt
@@ -7,7 +7,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result
import com.tangem.common.extensions.isZero
import com.tangem.tap.common.redux.AppState
-import com.tangem.tap.features.demo.DemoTransactionSender
+import com.tangem.domain.demo.DemoTransactionSender
import com.tangem.tap.features.demo.isDemoCard
import com.tangem.tap.features.send.redux.AmountActionUi
import com.tangem.tap.features.send.redux.FeeAction
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt
index dd10c08389..e85db98c7b 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt
@@ -31,7 +31,7 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TangemSigner
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
-import com.tangem.tap.features.demo.DemoTransactionSender
+import com.tangem.domain.demo.DemoTransactionSender
import com.tangem.tap.features.demo.isDemoCard
import com.tangem.tap.features.send.redux.*
import com.tangem.tap.features.send.redux.FeeAction.RequestFee
diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt
index 228273c9bd..c004c38b21 100644
--- a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt
+++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt
@@ -135,7 +135,7 @@ class MoonPayService(
.appendQueryParameter("apiKey", apiKey)
.appendQueryParameter("baseCurrencyCode", moonpayCurrency.currencyCode.uppercase())
.appendQueryParameter("refundWalletAddress", walletAddress)
- .appendQueryParameter("redirectURL", "tangem://redirect_sell")
+ .appendQueryParameter("redirectURL", "tangem://redirect_sell?currency_id=${cryptoCurrency.id.value}")
if (isDarkTheme) uri.appendQueryParameter("theme", "dark")
diff --git a/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/SellCurrencyDeepLink.kt b/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/SellCurrencyDeepLink.kt
index 579c72689a..db28975f63 100644
--- a/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/SellCurrencyDeepLink.kt
+++ b/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/SellCurrencyDeepLink.kt
@@ -11,6 +11,7 @@ class SellCurrencyDeepLink(val onReceive: (data: Data) -> Unit) : DeepLink {
transactionId = params["transactionId"] ?: return,
baseCurrencyAmount = params["baseCurrencyAmount"] ?: return,
depositWalletAddress = params["depositWalletAddress"] ?: return,
+ currencyId = params["currency_id"] ?: return,
depositWalletAddressTag = params["depositWalletAddressTag"],
)
@@ -21,6 +22,7 @@ class SellCurrencyDeepLink(val onReceive: (data: Data) -> Unit) : DeepLink {
val transactionId: String,
val baseCurrencyAmount: String,
val depositWalletAddress: String,
+ val currencyId: String,
val depositWalletAddressTag: String?,
)
}
\ No newline at end of file
diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json
index 84088baede..7da3c6d73d 100644
--- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json
+++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json
@@ -9,7 +9,7 @@
},
{
"name": "REDESIGNED_SEND_SCREEN_ENABLED",
- "version": "5.9.0"
+ "version": "5.9.1"
},
{
"name": "GENERAL_USER_WALLETS_LIST_MANAGER_ENABLED",
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 5d25d41f8e..5d5c08ef1a 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -51,6 +51,10 @@
Заводские настройки
Тип безопасности
Настройки карты
+ Ввиду особенностей сети Cardano при транзакции токена %1$s помимо комиссии сети будет списано %2$s
+ Для совершения транзакции %1$s, вам необходимо внести немного %2$s (%3$s), чтобы покрыть комиссию сети и минимальное значение ADA для отправки.
+ Недостаточно ADA для отправки токена
+ Вывод всего баланса ADA невозможен при наличии средств на токенах сети Cardano. Сначала выведите средства на ваших токенах.
Принять
Доступ запрещен
Применить
@@ -96,7 +100,8 @@
Нет адреса
OK
Основная карта
- Кодовая фраза
+ Парольная фраза
+ Вставить
Подробнее
Получить
Отклонить
@@ -183,9 +188,9 @@
Tangem предоставляет доступ к обмену через сторонних поставщиков в соответствии с их правилами
Выберите провайдера
Произошла ошибка. Код: %s
- К сожалению обмен указанной пары через выбранного провайдера на данный момент не возможен. Попробуйте совершить обмен позже. (Код: %s)
+ К сожалению, обмен указанной пары через выбранного провайдера на данный момент невозможен. Попробуйте совершить обмен позже. (Код: %s)
Выбранный провайдер не доступен для обмена. Попробуйте позже. (Код: %s)
- В данный момент обмен не возможен. Попробуйте позже. (Код: %s)
+ В данный момент обмен невозможен. Попробуйте позже. (Код: %s)
Курс обмена
Обмен через %s
Чтобы вернуть ваши деньги, посетите сайт провайдера
@@ -288,7 +293,7 @@
Ошибка активации
Вы добавили одну резервную карту. После того, как процесс будет завершен, Вы больше не сможете добавить карт. Если у Вас есть еще одна карта, добавьте ее в резервную копию. Хотите продолжить?
Процесс резервного копирования почти завершен. Вы не можете выйти из него сейчас.
- Кодовая фраза — это расширенная функция безопасности, которую используют криптокошельки. Она добавляет дополнительное слово или фразу по вашему выбору к уже существующей seed - фразе, чтобы разблокировать совершенно новый набор адресов.
+ Парольная фраза — это расширенная функция безопасности, которую используют криптокошельки. Она добавляет дополнительное слово или фразу по вашему выбору к уже существующей seed - фразе, чтобы разблокировать совершенно новый набор адресов.
Добавить резервную карту
Сканировать карту #%d
Создать резервную копию
@@ -478,7 +483,7 @@
Возможны задержки по транзакции
Из-за ограничений %1$s в одну транзакцию может поместиться только %2$s UTXO. Это означает, что вы можете отправить только %3$s или меньше. Вам нужно уменьшить сумму.
Лимит транзакции
- Необязательное
+ Опционально
Пожалуйста, совместите свой QR-код с квадратом, чтобы отсканировать его. Убедитесь, что вы сканируете адрес в сети %s.
Последние
Получатель
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index a099c577d4..04985255c2 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -50,6 +50,10 @@
Reset to Factory Settings
Security Mode
Card Settings
+ Due to the peculiarities of the Cardano network, when transacting the %1$s token, in addition to the network commission, %2$s will be charged
+ To make a %1$s transaction, you must deposit some %2$s (%3$s) to cover the network fee and minimum ADA value
+ Insufficient ADA to token transfer
+ Withdrawal of the entire ADA balance is not possible if funds are available in Cardano network tokens. First, withdraw funds on your tokens.
Accept
Access denied
Apply
@@ -96,6 +100,7 @@
OK
Primary Card
Passphrase
+ Paste
Read more
Receive
Reject
@@ -427,6 +432,8 @@
Reason: %1$s\nCode: %2$s
The transaction is not completed
Amount
+ Base fee
+ Represents the part of the transaction fee that goes to the miner
%1$s, %2$s
Address
Destination Tag
@@ -474,6 +481,8 @@
Due to %1$s limitations only %2$s UTXOs can fit in a single transaction. This means you can only send %3$s or less. You need to reduce the amount.
Transaction limitation
Optional
+ Priority fee
+ Represents the minimum gasUsed multiplier required for a transaction to be included in a block. This is the part of the transaction fee that is burnt.
Please align your QR code with the square to scan it. Ensure you scan %s network address.
Recent
Recipient
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt
index 712fc8cb52..f3cbfca76d 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt
@@ -47,14 +47,15 @@ inline fun SegmentedButtons(
selectedColor: Color = TangemTheme.colors.background.action,
dividerColor: Color = TangemTheme.colors.stroke.primary,
showIndication: Boolean = true,
- selectedItem: T? = null,
+ initialSelectedItem: T? = null,
+ isEnabled: Boolean = true,
crossinline buttonContent: @Composable (T) -> Unit,
) {
if (config.isEmpty() || config.size == 1) return
- var selected by remember {
- val selectedIndex = if (selectedItem == null) 0 else config.indexOf(selectedItem)
- mutableIntStateOf(selectedIndex)
+ var selectedIndex by remember {
+ val index = if (initialSelectedItem == null) 0 else config.indexOf(initialSelectedItem)
+ mutableIntStateOf(index)
}
Row(
@@ -69,7 +70,7 @@ inline fun SegmentedButtons(
val rightRadius = if (index == config.lastIndex) TangemTheme.dimens.radius26 else TangemTheme.dimens.radius0
val animateColor by animateColorAsState(
- targetValue = if (index == selected) selectedColor else color,
+ targetValue = if (index == selectedIndex) selectedColor else color,
label = "Segmented Button Selected Color Animation",
animationSpec = spring(stiffness = Spring.StiffnessMedium),
)
@@ -86,11 +87,12 @@ inline fun SegmentedButtons(
),
)
.clickable(
+ enabled = isEnabled,
interactionSource = remember { MutableInteractionSource() },
indication = if (showIndication) LocalIndication.current else null,
) {
+ selectedIndex = index
onClick(config[index])
- selected = index
},
) {
buttonContent.invoke(config[index])
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/fiaticon/FiatIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/fiaticon/FiatIcon.kt
index 3255b50af7..76d5c9d094 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/fiaticon/FiatIcon.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/fiaticon/FiatIcon.kt
@@ -4,11 +4,16 @@ import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.currency.DefaultCurrencyIcon
+private const val GRAY_SCALE_SATURATION = 0f
+private const val GRAY_SCALE_ALPHA = 0.4f
+
/**
* Simple icon from network
*
@@ -20,16 +25,19 @@ import com.tangem.core.ui.components.currency.DefaultCurrencyIcon
fun FiatIcon(
url: String?,
size: Dp,
+ isGrayscale: Boolean,
modifier: Modifier = Modifier,
@DrawableRes fallbackResId: Int = R.drawable.ic_shape_circle,
) {
val iconData: Any = if (url.isNullOrBlank()) fallbackResId else url
+ val alpha = if (isGrayscale) GRAY_SCALE_ALPHA else 1f
+ val colorFilter = if (isGrayscale) GrayscaleColorFilter else null
DefaultCurrencyIcon(
iconData = iconData,
size = size,
- alpha = 1f,
- colorFilter = null,
+ alpha = alpha,
+ colorFilter = colorFilter,
errorIcon = {
Image(
painter = painterResource(id = fallbackResId),
@@ -38,4 +46,7 @@ fun FiatIcon(
},
modifier = modifier,
)
-}
\ No newline at end of file
+}
+
+private val GrayscaleColorFilter: ColorFilter
+ get() = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(GRAY_SCALE_SATURATION) })
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/tokenicon/converter/CryptoCurrencyToIconStateConverter.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/tokenicon/converter/CryptoCurrencyToIconStateConverter.kt
index 19703e82b9..fbcc6e3caa 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/tokenicon/converter/CryptoCurrencyToIconStateConverter.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/tokenicon/converter/CryptoCurrencyToIconStateConverter.kt
@@ -20,6 +20,26 @@ class CryptoCurrencyToIconStateConverter : Converter getIconStateForCoin(
+ coin = currency,
+ isUnreachable = value.value.isError,
+ forceGrayscale = forceGrayscale,
+ )
+ is CryptoCurrency.Token -> getIconStateForToken(
+ token = currency,
+ isErrorStatus = value.value.isError,
+ forceGrayscale = forceGrayscale,
+ showCustomBadge = showCustomTokenBadge,
+ )
+ }
+ }
+
fun convert(currency: CryptoCurrency): TokenIconState {
return when (currency) {
is CryptoCurrency.Coin -> getIconStateForCoin(currency, isUnreachable = false)
@@ -27,18 +47,27 @@ class CryptoCurrencyToIconStateConverter : Converter Unit, modifi
modifier = modifier,
) {
Text(
- text = "Paste",
+ text = stringResource(R.string.common_paste),
style = TangemTheme.typography.button,
color = TangemTheme.colors.text.primary2,
modifier = Modifier
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionDoneTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionDoneTitle.kt
index 2adcbdc369..6f23701fea 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionDoneTitle.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionDoneTitle.kt
@@ -16,7 +16,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.res.TangemTheme
-import com.tangem.core.ui.utils.toDateFormat
+import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.core.ui.utils.toTimeFormat
/**
@@ -47,7 +47,11 @@ fun TransactionDoneTitle(@StringRes titleRes: Int, date: Long, modifier: Modifie
.padding(top = TangemTheme.dimens.spacing16),
)
Text(
- text = stringResource(id = R.string.send_date_format, date.toDateFormat(), date.toTimeFormat()),
+ text = stringResource(
+ id = R.string.send_date_format,
+ date.toTimeFormat(DateTimeFormatters.dateFormatter),
+ date.toTimeFormat(),
+ ),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier
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 42309223b1..b724559114 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
@@ -40,6 +40,29 @@ object BigDecimalFormatter {
}
}
+ fun formatCryptoAmountUncapped(
+ cryptoAmount: BigDecimal?,
+ cryptoCurrency: CryptoCurrency,
+ locale: Locale = Locale.getDefault(),
+ ): String {
+ if (cryptoAmount == null) return EMPTY_BALANCE_SIGN
+
+ val formatter = NumberFormat.getNumberInstance(locale).apply {
+ maximumFractionDigits = cryptoCurrency.decimals
+ minimumFractionDigits = 2
+ isGroupingUsed = true
+ roundingMode = RoundingMode.DOWN
+ }
+
+ return formatter.format(cryptoAmount).let {
+ if (cryptoCurrency.symbol.isEmpty()) {
+ it
+ } else {
+ it + "\u2009${cryptoCurrency.symbol}"
+ }
+ }
+ }
+
fun formatCryptoAmount(
cryptoAmount: BigDecimal?,
cryptoCurrency: CryptoCurrency,
diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/DateUtils.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/DateUtils.kt
index 8d596fc41f..bc2560f6e9 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/utils/DateUtils.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/utils/DateUtils.kt
@@ -11,7 +11,7 @@ import org.joda.time.format.DateTimeFormatter
* If [this] timestamp is today or yesterday, returns relative date,
* otherwise returns formatting date.
*/
-fun Long.toDateFormat(formatter: DateTimeFormatter = DateTimeFormatters.dateFormatter): String {
+fun Long.toDateFormatWithTodayYesterday(formatter: DateTimeFormatter = DateTimeFormatters.dateFormatter): String {
val localDate = DateTime(this, DateTimeZone.getDefault())
return if (localDate.isToday() || localDate.isYesterday()) {
DateUtils.getRelativeTimeSpanString(
diff --git a/domain/demo/build.gradle.kts b/domain/demo/build.gradle.kts
index f0c56e2a73..a9e6de5249 100644
--- a/domain/demo/build.gradle.kts
+++ b/domain/demo/build.gradle.kts
@@ -10,4 +10,5 @@ android {
dependencies {
implementation(deps.tangem.blockchain)
+ implementation(deps.tangem.card.core)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/demo/DemoTransactionSender.kt b/domain/demo/src/main/java/com/tangem/domain/demo/DemoTransactionSender.kt
similarity index 98%
rename from app/src/main/java/com/tangem/tap/features/demo/DemoTransactionSender.kt
rename to domain/demo/src/main/java/com/tangem/domain/demo/DemoTransactionSender.kt
index faecf5aac1..e98373f775 100644
--- a/app/src/main/java/com/tangem/tap/features/demo/DemoTransactionSender.kt
+++ b/domain/demo/src/main/java/com/tangem/domain/demo/DemoTransactionSender.kt
@@ -1,4 +1,4 @@
-package com.tangem.tap.features.demo
+package com.tangem.domain.demo
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt
index 87015b1305..db909d08f3 100644
--- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt
@@ -184,8 +184,9 @@ sealed class CryptoCurrency : Parcelable {
}
private companion object {
+ // should use delimiters that could be used in URL not like path or query delimiters
const val PREFIX_DELIMITER = '_'
- const val SUFFIX_DELIMITER = '#'
+ const val SUFFIX_DELIMITER = ';'
const val DERIVATION_PATH_DELIMITER = 'd'
}
}
diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SendTransactionError.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SendTransactionError.kt
index 22b7991290..3f5f7485d4 100644
--- a/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SendTransactionError.kt
+++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SendTransactionError.kt
@@ -4,7 +4,7 @@ import com.tangem.core.ui.extensions.TextReference
sealed class SendTransactionError {
- object DemoCardError : SendTransactionError()
+ data object DemoCardError : SendTransactionError()
data class DataError(val message: String?) : SendTransactionError()
@@ -12,7 +12,7 @@ sealed class SendTransactionError {
data class BlockchainSdkError(val code: Int, val message: String?) : SendTransactionError()
- object UserCancelledError : SendTransactionError()
+ data object UserCancelledError : SendTransactionError()
data class CreateAccountUnderfunded(val amount: String) : SendTransactionError()
diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/GetFeeUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/GetFeeUseCase.kt
index ae697ceb08..2f113a02a0 100644
--- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/GetFeeUseCase.kt
+++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/GetFeeUseCase.kt
@@ -6,10 +6,12 @@ import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.extensions.Result
+import com.tangem.domain.demo.DemoConfig
+import com.tangem.domain.demo.DemoTransactionSender
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.walletmanager.WalletManagersFacade
-import com.tangem.domain.wallets.models.UserWalletId
+import com.tangem.domain.wallets.models.UserWallet
import java.math.BigDecimal
/**
@@ -17,23 +19,31 @@ import java.math.BigDecimal
*/
class GetFeeUseCase(
private val walletManagersFacade: WalletManagersFacade,
+ private val demoConfig: DemoConfig,
) {
suspend operator fun invoke(
amount: BigDecimal,
destination: String,
- userWalletId: UserWalletId,
+ userWallet: UserWallet,
cryptoCurrency: CryptoCurrency,
) = either {
catch(
block = {
- val result = requireNotNull(
- walletManagersFacade.getFee(
- amount = convertCryptoCurrencyToAmount(cryptoCurrency, amount),
+ val amountData = convertCryptoCurrencyToAmount(cryptoCurrency, amount)
+
+ val result = if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) {
+ demoTransactionSender(userWallet, cryptoCurrency).getFee(
+ amount = amountData,
destination = destination,
- userWalletId = userWalletId,
+ )
+ } else {
+ walletManagersFacade.getFee(
+ amount = amountData,
+ destination = destination,
+ userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
- ),
- ) { "Fee is null" }
+ ) ?: error("Fee is null")
+ }
val maybeFee = when (result) {
is Result.Success -> result.data
@@ -47,6 +57,17 @@ class GetFeeUseCase(
)
}
+ private suspend fun demoTransactionSender(
+ userWallet: UserWallet,
+ cryptoCurrency: CryptoCurrency,
+ ): DemoTransactionSender {
+ return DemoTransactionSender(
+ walletManagersFacade
+ .getOrCreateWalletManager(userWallet.walletId, cryptoCurrency.network)
+ ?: error("WalletManager is null"),
+ )
+ }
+
private fun convertCryptoCurrencyToAmount(cryptoCurrency: CryptoCurrency, amount: BigDecimal) = Amount(
currencySymbol = cryptoCurrency.symbol,
value = amount,
diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt
index d2c9cbd085..379d90d4be 100644
--- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt
+++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt
@@ -5,6 +5,7 @@ import arrow.core.left
import arrow.core.right
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.TransactionData
+import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.network.ResultChecker
import com.tangem.common.core.TangemSdkError
@@ -12,20 +13,23 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
-import com.tangem.domain.demo.IsDemoCardUseCase
+import com.tangem.domain.demo.DemoConfig
+import com.tangem.domain.demo.DemoTransactionSender
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.transaction.R
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.domain.transaction.error.SendTransactionError.Companion.USER_CANCELLED_ERROR_CODE
+import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.sdk.extensions.localizedDescriptionRes
import com.tangem.utils.toFormattedString
class SendTransactionUseCase(
- private val isDemoCardUseCase: IsDemoCardUseCase,
+ private val demoConfig: DemoConfig,
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val transactionRepository: TransactionRepository,
+ private val walletManagersFacade: WalletManagersFacade,
) {
suspend operator fun invoke(
txData: TransactionData,
@@ -39,8 +43,13 @@ class SendTransactionUseCase(
cardSdkConfigRepository.setLinkedTerminal(false)
}
val sendResult = try {
- if (isDemoCardUseCase(cardId = userWallet.cardId)) {
- SendTransactionError.DemoCardError.left()
+ if (demoConfig.isDemoCardId(cardId = userWallet.cardId)) {
+ sendDemo(
+ userWallet = userWallet,
+ network = network,
+ transactionData = txData,
+ signer = signer,
+ )
} else {
transactionRepository.sendTransaction(
txData = txData,
@@ -66,6 +75,27 @@ class SendTransactionUseCase(
)
}
+ private suspend fun sendDemo(
+ userWallet: UserWallet,
+ network: Network,
+ transactionData: TransactionData,
+ signer: TransactionSigner,
+ ): Either {
+ val demoTransactionSender = DemoTransactionSender(
+ walletManagersFacade
+ .getOrCreateWalletManager(userWallet.walletId, network)
+ ?: error("WalletManager is null"),
+ )
+
+ val result = demoTransactionSender.send(transactionData = transactionData, signer = signer)
+
+ return if (result is SimpleResult.Failure && result.error.customMessage.contains(DemoTransactionSender.ID)) {
+ SendTransactionError.DemoCardError.left()
+ } else {
+ result.right()
+ }
+ }
+
private fun handleError(result: SimpleResult.Failure): SendTransactionError {
if (ResultChecker.isNetworkError(result)) {
return SendTransactionError.NetworkError(
diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/YourSeedPhraseScreen.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/YourSeedPhraseScreen.kt
index 94f072a0ac..609e2df784 100644
--- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/YourSeedPhraseScreen.kt
+++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/YourSeedPhraseScreen.kt
@@ -93,7 +93,7 @@ private fun SegmentSeedBlock(state: SegmentSeedState, modifier: Modifier = Modif
SegmentedButtons(
modifier = modifier.padding(horizontal = TangemTheme.dimens.spacing76),
config = state.seedSegments,
- selectedItem = state.selectedSeedType,
+ initialSelectedItem = state.selectedSeedType,
onClick = {
state.onSelectType(it)
},
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt
index f3f304b7ff..0938d051f2 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt
@@ -117,6 +117,7 @@ internal class SendStateFactory(
recipientState = state.recipientState
?: recipientStateConverter.convert(SendRecipientStateConverter.Data(destinationAddress, memo)),
feeState = state.feeState ?: feeStateConverter.convert(Unit),
+ sendState = confirmStateConverter.convert(Unit),
isEditingDisabled = true,
cryptoCurrencyName = cryptoCurrencyStatusProvider().currency.name,
)
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt
index 586c55c978..eb0e05e900 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt
@@ -48,6 +48,7 @@ internal sealed class SendStates {
val walletBalance: TextReference,
val tokenIconState: TokenIconState,
val segmentedButtonConfig: PersistentList,
+ val isSegmentedButtonsEnabled: Boolean,
val amountTextField: SendTextField.AmountField,
val notifications: ImmutableList,
val appCurrencyCode: String,
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt
index f06a20bf48..ba3ab64dcf 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt
@@ -31,6 +31,7 @@ internal class SendAmountStateConverter(
val status = cryptoCurrencyStatusProvider()
val fiat = formatFiatAmount(status.value.fiatAmount, appCurrency.code, appCurrency.symbol)
val crypto = formatCryptoAmount(status.value.amount, status.currency.symbol, status.currency.decimals)
+ val noFeeRate = status.value.fiatRate.isNullOrZero()
return SendStates.AmountState(
walletName = userWallet.name,
@@ -42,22 +43,23 @@ internal class SendAmountStateConverter(
isFeeLoading = false,
appCurrencyCode = appCurrency.code,
subtractedFee = null,
- segmentedButtonConfig = if (status.value.fiatRate.isNullOrZero()) {
- persistentListOf()
- } else {
- persistentListOf(
- SendAmountSegmentedButtonsConfig(
- title = stringReference(status.currency.symbol),
- iconState = iconStateConverter.convert(status),
- isFiat = false,
+ segmentedButtonConfig = persistentListOf(
+ SendAmountSegmentedButtonsConfig(
+ title = stringReference(status.currency.symbol),
+ iconState = iconStateConverter.convertCustom(
+ value = status,
+ forceGrayscale = noFeeRate,
+ showCustomTokenBadge = false,
),
- SendAmountSegmentedButtonsConfig(
- title = stringReference(appCurrency.code),
- iconUrl = appCurrency.iconSmallUrl,
- isFiat = true,
- ),
- )
- },
+ isFiat = false,
+ ),
+ SendAmountSegmentedButtonsConfig(
+ title = stringReference(appCurrency.code),
+ iconUrl = appCurrency.iconSmallUrl,
+ isFiat = true,
+ ),
+ ),
+ isSegmentedButtonsEnabled = !noFeeRate,
)
}
}
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountSubtractConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountSubtractConverter.kt
index bf0f52dd73..fb5d2708ad 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountSubtractConverter.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountSubtractConverter.kt
@@ -19,11 +19,13 @@ internal class SendAmountSubtractConverter(
val feeValue = feeState.fee?.amount?.value ?: return state
val amountTextField = amountState.amountTextField
val amountValue = amountTextField.cryptoAmount.value ?: return state
+ val balance = cryptoCurrencyStatus.value.amount ?: return state
val fiatRate = cryptoCurrencyStatus.value.fiatRate
val cryptoDecimals = amountTextField.cryptoAmount.decimals
val fiatDecimals = amountTextField.fiatAmount.decimals
- val feeDiff = amountState.subtractedFee?.let { feeValue.minus(it) } ?: feeValue
+ val feeDiff = amountState.subtractedFee?.let { feeValue.minus(it) }
+ ?: amountValue.minus(balance.minus(feeValue))
val decimalCryptoValue = amountValue.minus(feeDiff)
if (decimalCryptoValue < BigDecimal.ZERO) return state
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt
index 0d147eb6fd..3ad03b434c 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt
@@ -20,10 +20,10 @@ import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents
import com.tangem.features.send.impl.presentation.state.*
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
import com.tangem.features.send.impl.presentation.state.fee.FeeType
+import com.tangem.features.send.impl.presentation.state.fee.checkIfFeeTooHigh
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
import com.tangem.lib.crypto.BlockchainUtils.isDogecoin
import com.tangem.utils.Provider
-import com.tangem.utils.isNullOrZero
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@@ -31,7 +31,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import java.math.BigDecimal
-import java.math.BigInteger
@Suppress("LongParameterList")
internal class SendNotificationFactory(
@@ -57,6 +56,7 @@ internal class SendNotificationFactory(
val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO
buildList {
// errors
+ addFeeUnreachableNotification(feeState.feeSelectorState)
addExceedBalanceNotification(feeAmount, amountValue)
addExceedsBalanceNotification(feeState.fee)
addMinimumAmountErrorNotification(feeAmount, amountValue)
@@ -70,7 +70,7 @@ internal class SendNotificationFactory(
}.toImmutableList()
}
- fun dismissNotificationState(clazz: Class): SendUiState {
+ fun dismissNotificationState(clazz: Class, isIgnored: Boolean = false): SendUiState {
val state = currentStateProvider()
val sendState = state.sendState ?: return state
val notificationsToRemove = sendState.notifications.filterIsInstance(clazz)
@@ -78,50 +78,35 @@ internal class SendNotificationFactory(
updatedNotifications.removeAll(notificationsToRemove)
return state.copy(
sendState = sendState.copy(
- ignoreAmountReduce = true,
+ ignoreAmountReduce = isIgnored,
notifications = updatedNotifications.toImmutableList(),
),
)
}
+ private fun MutableList.addFeeUnreachableNotification(feeSelectorState: FeeSelectorState) {
+ if (feeSelectorState is FeeSelectorState.Error) {
+ add(SendNotification.Warning.NetworkFeeUnreachable(clickIntents::feeReload))
+ }
+ }
+
private fun MutableList.addExceedBalanceNotification(
feeAmount: BigDecimal,
receivedAmount: BigDecimal,
) {
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
- val coinCryptoCurrencyStatus = coinCryptoCurrencyStatusProvider()
val feePaidCryptoCurrencyStatus = feePaidCryptoCurrencyStatusProvider()
val cryptoAmount = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
- val coinCryptoAmount = coinCryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
- val showNotification = if (cryptoCurrencyStatus.currency.id == feePaidCryptoCurrencyStatus?.currency?.id) {
- receivedAmount > cryptoAmount || feeAmount > coinCryptoAmount
- } else {
- receivedAmount + feeAmount > cryptoAmount
- }
+ val isCurrentNotFeePaidCurrency = cryptoCurrencyStatus.currency.id != feePaidCryptoCurrencyStatus?.currency?.id
+ if (isCurrentNotFeePaidCurrency) return
+ val showNotification = receivedAmount + feeAmount > cryptoAmount
if (showNotification) {
add(SendNotification.Error.TotalExceedsBalance)
}
}
- private fun MutableList.addMinimumAmountErrorNotification(
- feeAmount: BigDecimal,
- receivedAmount: BigDecimal,
- ) {
- val coinCryptoCurrencyStatus = coinCryptoCurrencyStatusProvider()
-
- val totalAmount = feeAmount + receivedAmount
- val balance = coinCryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
-
- if (isDogecoin(coinCryptoCurrencyStatus.currency.network.id.value)) {
- val minimum = BigDecimal(DOGECOIN_MINIMUM)
- if (receivedAmount < minimum || balance - totalAmount < minimum) {
- add(SendNotification.Error.MinimumAmountError(DOGECOIN_MINIMUM))
- }
- }
- }
-
// todo temporarily disabling notification
// private suspend fun MutableList.addReserveAmountErrorNotification(recipientAddress: String) {
// val userWalletId = userWalletProvider().walletId
@@ -198,7 +183,7 @@ internal class SendNotificationFactory(
if (currencyDeposit != null && currencyDeposit > diff) {
add(
SendNotification.Error.ExistentialDeposit(
- BigDecimalFormatter.formatCryptoAmount(
+ BigDecimalFormatter.formatCryptoAmountUncapped(
cryptoAmount = currencyDeposit,
cryptoCurrency = cryptoCurrency,
),
@@ -233,6 +218,20 @@ internal class SendNotificationFactory(
}
}
+ private fun MutableList.addMinimumAmountErrorNotification(
+ feeAmount: BigDecimal,
+ receivedAmount: BigDecimal,
+ ) {
+ val coinCryptoCurrencyStatus = coinCryptoCurrencyStatusProvider()
+ val minimum = BigDecimal(DOGECOIN_MINIMUM)
+
+ val isDogecoin = isDogecoin(coinCryptoCurrencyStatus.currency.network.id.value)
+ val isExceedDustLimit = checkDustLimits(feeAmount, receivedAmount, minimum)
+ if (isDogecoin && isExceedDustLimit) {
+ add(SendNotification.Error.MinimumAmountError(DOGECOIN_MINIMUM))
+ }
+ }
+
private suspend fun MutableList.addDustWarningNotification(
feeAmount: BigDecimal,
receivedAmount: BigDecimal,
@@ -241,18 +240,12 @@ internal class SendNotificationFactory(
val dustValue = currencyChecksRepository.getDustValue(
userWalletProvider().walletId,
cryptoCurrencyStatus.currency.network,
- )
- val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
- if (dustValue != null && !balance.isNullOrZero() && receivedAmount < balance) {
- val totalAmount = feeAmount + receivedAmount
- val change = balance - totalAmount
- val isChangeLowerThanDust = change < dustValue && change != BigDecimal.ZERO
- val isShowWarning = totalAmount < dustValue || isChangeLowerThanDust
- if (isShowWarning) {
- add(
- SendNotification.Error.MinimumAmountError(dustValue.toPlainString()),
- )
- }
+ ) ?: return
+
+ if (checkDustLimits(feeAmount, receivedAmount, dustValue)) {
+ add(
+ SendNotification.Error.MinimumAmountError(dustValue.toPlainString()),
+ )
}
}
@@ -274,13 +267,9 @@ internal class SendNotificationFactory(
private fun MutableList.addTooHighNotification(feeSelectorState: FeeSelectorState) {
if (feeSelectorState !is FeeSelectorState.Content) return
- val multipleFees = feeSelectorState.fees as? TransactionFee.Choosable ?: return
- val highValue = multipleFees.priority.amount.value ?: return
- val customAmount = feeSelectorState.customValues.firstOrNull() ?: return
- val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
- val diff = (customValue / highValue).toBigInteger()
- if (feeSelectorState.selectedFee == FeeType.Custom && diff > FEE_MAX_DIFF) {
- add(SendNotification.Warning.TooHigh(diff.toString()))
+
+ checkIfFeeTooHigh(feeSelectorState) { diff ->
+ add(SendNotification.Warning.TooHigh(diff))
}
}
@@ -361,8 +350,17 @@ internal class SendNotificationFactory(
return Blockchain.fromNetworkId(this.currency.network.backendId) == Blockchain.Arbitrum
}
+ private fun checkDustLimits(feeAmount: BigDecimal, receivedAmount: BigDecimal, dustValue: BigDecimal): Boolean {
+ val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
+ val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
+
+ val totalAmount = feeAmount + receivedAmount
+ val change = balance - totalAmount
+ val isChangeLowerThanDust = change < dustValue && change > BigDecimal.ZERO
+ return receivedAmount < dustValue || isChangeLowerThanDust
+ }
+
companion object {
private const val DOGECOIN_MINIMUM = "0.01"
- internal val FEE_MAX_DIFF = BigInteger("5")
}
}
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt
index 57e567a586..31307124fd 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt
@@ -1,10 +1,12 @@
package com.tangem.features.send.impl.presentation.state.fee
import com.tangem.blockchain.common.transaction.TransactionFee
+import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.SendUiState
-import com.tangem.features.send.impl.presentation.state.confirm.SendNotificationFactory
+import java.math.BigDecimal
+import java.math.RoundingMode
/**
* Check if sending amount with fee is greater than balance
@@ -32,14 +34,16 @@ internal fun checkIfFeeTooLow(state: SendUiState): Boolean {
/**
* Check if custom fee is too high
*/
-internal fun checkIfFeeTooHigh(state: SendUiState, onShow: (String) -> Unit): Boolean {
- val feeSelectorState = state.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return false
+internal fun checkIfFeeTooHigh(feeSelectorState: FeeSelectorState.Content, onShow: (String) -> Unit): Boolean {
val multipleFees = feeSelectorState.fees as? TransactionFee.Choosable ?: return false
val highValue = multipleFees.priority.amount.value ?: return false
val customAmount = feeSelectorState.customValues.firstOrNull() ?: return false
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
- val diff = (customValue / highValue).toBigInteger()
- val isShow = feeSelectorState.selectedFee == FeeType.Custom && diff > SendNotificationFactory.FEE_MAX_DIFF
- if (isShow) onShow(diff.toString())
+ val diff = customValue / highValue
+ val isShow = feeSelectorState.selectedFee == FeeType.Custom && diff > FEE_MAX_DIFF
+ if (isShow) onShow(diff.parseBigDecimal(ZERO_DECIMALS, RoundingMode.HALF_UP))
return isShow
-}
\ No newline at end of file
+}
+
+private val FEE_MAX_DIFF = BigDecimal("5")
+private const val ZERO_DECIMALS = 0
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt
index 67fe86cf8e..cd1fe29939 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt
@@ -15,6 +15,8 @@ internal sealed class FeeSelectorState {
val customValues: ImmutableList = persistentListOf(),
) : FeeSelectorState()
+ data object Loading : FeeSelectorState()
+
data object Error : FeeSelectorState()
}
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt
index ba181f423f..5e34a54d61 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt
@@ -47,6 +47,11 @@ internal class FeeStateFactory(
return state.copy(
amountState = state.amountState?.copy(isFeeLoading = true),
feeState = feeState.copy(
+ feeSelectorState = if (feeState.feeSelectorState is FeeSelectorState.Content) {
+ feeState.feeSelectorState
+ } else {
+ FeeSelectorState.Loading
+ },
notifications = persistentListOf(),
isPrimaryButtonEnabled = false,
),
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeStateConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeStateConverter.kt
index d9b92b387d..f0cdd28425 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeStateConverter.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeStateConverter.kt
@@ -14,7 +14,7 @@ internal class SendFeeStateConverter(
override fun convert(value: Unit): SendStates.FeeState {
return SendStates.FeeState(
- feeSelectorState = FeeSelectorState.Error,
+ feeSelectorState = FeeSelectorState.Loading,
fee = null,
notifications = persistentListOf(),
rate = feeCryptoCurrencyStatusProvider()?.value?.fiatRate,
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/AmountStatePreviewData.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/AmountStatePreviewData.kt
index 91dbbde9bf..c301029957 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/AmountStatePreviewData.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/AmountStatePreviewData.kt
@@ -49,6 +49,7 @@ internal object AmountStatePreviewData {
isError = false,
error = TextReference.EMPTY,
),
+ isSegmentedButtonsEnabled = true,
)
val fiatAmountState = amountState.copy(
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientHistoryListConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientHistoryListConverter.kt
index f6ccb6ba98..3b555a7e21 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientHistoryListConverter.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientHistoryListConverter.kt
@@ -4,7 +4,7 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.utils.DateTimeFormatters
-import com.tangem.core.ui.utils.toDateFormat
+import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@@ -81,7 +81,7 @@ internal class SendRecipientHistoryListConverter(
}
private fun TxHistoryItem.extractTimestamp(): TextReference {
- val date = timestampInMillis.toDateFormat(
+ val date = timestampInMillis.toDateFormatWithTodayYesterday(
formatter = DateTimeFormatters.dateDDMMYYYY,
)
val time = timestampInMillis.toTimeFormat()
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt
index 855e5d3a3b..6b92680baf 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt
@@ -18,22 +18,25 @@ internal class SendRecipientWalletListConverter :
}
}
- private fun List.filterWallets() = this.filterNotNull()
- .groupBy { item -> item.name }
- .values.map {
- it.mapIndexed { index, item ->
- val name = if (it.size > 1) {
- "${item.name} ${index.inc()}"
- } else {
- item.name
+ private fun List.filterWallets(): PersistentList {
+ var walletsCounter = 0
+ return this.filterNotNull()
+ .groupBy { item -> item.name }
+ .values.map {
+ it.mapIndexed { index, item ->
+ val name = if (it.size > 1) {
+ "${item.name} ${index.inc()}"
+ } else {
+ item.name
+ }
+ SendRecipientListContent(
+ id = "${WALLET_KEY_TAG}${walletsCounter++}",
+ title = TextReference.Str(item.address),
+ subtitle = TextReference.Str(name),
+ )
}
- SendRecipientListContent(
- id = "${WALLET_KEY_TAG}$index",
- title = TextReference.Str(item.address),
- subtitle = TextReference.Str(name),
- )
}
- }
- .flatten()
- .toPersistentList()
+ .flatten()
+ .toPersistentList()
+ }
}
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountButtons.kt
index 00af1e90e7..4e05fe9506 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountButtons.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountButtons.kt
@@ -29,6 +29,7 @@ internal fun LazyListScope.buttons(
segmentedButtonConfig: PersistentList,
clickIntents: SendClickIntents,
isMaxButtonEnabled: Boolean,
+ isSegmentedButtonsEnabled: Boolean,
) {
item(
key = AMOUNT_BUTTONS_KEY,
@@ -48,8 +49,12 @@ internal fun LazyListScope.buttons(
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
clickIntents.onCurrencyChangeClick(it.isFiat)
},
+ isEnabled = isSegmentedButtonsEnabled,
) {
- SendAmountCurrencyButton(it)
+ SendAmountCurrencyButton(
+ button = it,
+ isSegmentedButtonsEnabled = isSegmentedButtonsEnabled,
+ )
}
} else {
SpacerWMax()
@@ -72,7 +77,7 @@ internal fun LazyListScope.buttons(
}
@Composable
-private fun SendAmountCurrencyButton(button: SendAmountSegmentedButtonsConfig) {
+private fun SendAmountCurrencyButton(button: SendAmountSegmentedButtonsConfig, isSegmentedButtonsEnabled: Boolean) {
Row(
modifier = Modifier
.fillMaxSize()
@@ -86,17 +91,16 @@ private fun SendAmountCurrencyButton(button: SendAmountSegmentedButtonsConfig) {
FiatIcon(
url = button.iconUrl,
size = TangemTheme.dimens.size18,
+ isGrayscale = !isSegmentedButtonsEnabled,
modifier = Modifier.size(TangemTheme.dimens.size18),
)
- } else {
- button.iconState?.let {
- TokenIcon(
- state = it,
- shouldDisplayNetwork = false,
- modifier = Modifier
- .size(TangemTheme.dimens.size18),
- )
- }
+ } else if (button.iconState != null) {
+ TokenIcon(
+ state = button.iconState,
+ shouldDisplayNetwork = false,
+ modifier = Modifier
+ .size(TangemTheme.dimens.size18),
+ )
}
Text(
text = button.title.resolveReference(),
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt
index 0f5b6493e8..fb9d2df22c 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt
@@ -36,6 +36,7 @@ internal fun SendAmountContent(
segmentedButtonConfig = amountState.segmentedButtonConfig,
clickIntents = clickIntents,
isMaxButtonEnabled = !amountState.isFeeLoading,
+ isSegmentedButtonsEnabled = amountState.isSegmentedButtonsEnabled,
)
notifications(amountState.notifications)
}
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt
index a3ee42ccbb..11423e7c73 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt
@@ -1,21 +1,22 @@
package com.tangem.features.send.impl.presentation.ui.send
+import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.rows.SelectorRowItem
import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.state.SendStates
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
@@ -26,20 +27,12 @@ import com.tangem.features.send.impl.presentation.utils.getFiatReference
@Composable
internal fun FeeBlock(feeState: SendStates.FeeState, isSuccess: Boolean, onClick: () -> Unit) {
- val fee = feeState.fee ?: return
- val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return
- val (title, icon) = when (feeSelectorState.selectedFee) {
- FeeType.Slow -> R.string.common_fee_selector_option_slow to R.drawable.ic_tortoise_24
- FeeType.Market -> R.string.common_fee_selector_option_market to R.drawable.ic_bird_24
- FeeType.Fast -> R.string.common_fee_selector_option_fast to R.drawable.ic_hare_24
- FeeType.Custom -> R.string.common_fee_selector_option_custom to R.drawable.ic_edit_24
- }
Column(
modifier = Modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.action)
- .clickable(enabled = !isSuccess) { onClick() }
+ .clickable(enabled = !isSuccess && feeState.fee != null) { onClick() }
.padding(TangemTheme.dimens.spacing12),
) {
Text(
@@ -47,17 +40,71 @@ internal fun FeeBlock(feeState: SendStates.FeeState, isSuccess: Boolean, onClick
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
)
- SelectorRowItem(
- titleRes = title,
- iconRes = icon,
- preDot = getCryptoReference(fee.amount, feeState.isFeeApproximate),
- postDot = getFiatReference(fee.amount, feeState.rate, feeState.appCurrency),
- ellipsizeOffset = fee.amount.currencySymbol.length,
- isSelected = true,
- showDivider = false,
- paddingValues = PaddingValues(),
+
+ Box(
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
- )
+ ) {
+ val feeSelectorState = feeState.feeSelectorState
+ val feeAmount = feeState.fee?.amount
+ val (title, icon) = if (feeSelectorState is FeeSelectorState.Content) {
+ when (feeSelectorState.selectedFee) {
+ FeeType.Slow -> R.string.common_fee_selector_option_slow to R.drawable.ic_tortoise_24
+ FeeType.Market -> R.string.common_fee_selector_option_market to R.drawable.ic_bird_24
+ FeeType.Fast -> R.string.common_fee_selector_option_fast to R.drawable.ic_hare_24
+ FeeType.Custom -> R.string.common_fee_selector_option_custom to R.drawable.ic_edit_24
+ }
+ } else {
+ R.string.common_fee_selector_option_market to R.drawable.ic_bird_24
+ }
+ SelectorRowItem(
+ titleRes = title,
+ iconRes = icon,
+ preDot = getCryptoReference(feeAmount, feeState.isFeeApproximate),
+ postDot = feeAmount?.let { getFiatReference(it, feeState.rate, feeState.appCurrency) },
+ ellipsizeOffset = feeAmount?.currencySymbol?.length,
+ isSelected = true,
+ showDivider = false,
+ paddingValues = PaddingValues(),
+ )
+ FeeLoading(feeSelectorState)
+ FeeError(feeSelectorState)
+ }
+ }
+}
+
+@Composable
+private fun BoxScope.FeeLoading(feeSelectorState: FeeSelectorState) {
+ AnimatedContent(
+ targetState = feeSelectorState,
+ label = "Fee Loading State Change",
+ modifier = Modifier.align(Alignment.CenterEnd),
+ ) {
+ if (it == FeeSelectorState.Loading) {
+ RectangleShimmer(
+ radius = TangemTheme.dimens.radius3,
+ modifier = Modifier.size(
+ height = TangemTheme.dimens.size12,
+ width = TangemTheme.dimens.size90,
+ ),
+ )
+ }
+ }
+}
+
+@Composable
+private fun BoxScope.FeeError(feeSelectorState: FeeSelectorState) {
+ AnimatedContent(
+ targetState = feeSelectorState,
+ label = "Fee Error State Change",
+ modifier = Modifier.align(Alignment.CenterEnd),
+ ) {
+ if (it == FeeSelectorState.Error) {
+ Text(
+ text = BigDecimalFormatter.EMPTY_BALANCE_SIGN,
+ color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography.body2,
+ )
+ }
}
}
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/RecipientBlock.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/RecipientBlock.kt
index f276edf3c1..0801e63cd6 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/RecipientBlock.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/RecipientBlock.kt
@@ -4,6 +4,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -40,6 +41,7 @@ internal fun RecipientBlock(
.padding(TangemTheme.dimens.spacing12),
) {
AddressBlock(recipientState.addressTextField)
+ MemoBlock(recipientState.memoTextField)
}
}
@@ -70,6 +72,28 @@ private fun AddressBlock(address: SendTextField.RecipientAddress) {
}
}
+@Composable
+private fun MemoBlock(memo: SendTextField.RecipientMemo?) {
+ val showMemo = memo != null && memo.value.isNotBlank()
+ if (showMemo) {
+ HorizontalDivider(
+ color = TangemTheme.colors.icon.inactive,
+ modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing12),
+ )
+ Text(
+ text = memo?.label?.resolveReference().orEmpty(),
+ style = TangemTheme.typography.caption2,
+ color = TangemTheme.colors.text.secondary,
+ )
+ Text(
+ text = memo?.value.orEmpty(),
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.primary1,
+ modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
+ )
+ }
+}
+
// region Preview
@Preview
@Composable
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt
index ffad3f3633..77708a0eda 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt
@@ -24,6 +24,7 @@ import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
+import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
import com.tangem.domain.tokens.utils.convertToAmount
import com.tangem.domain.transaction.error.GetFeeError
@@ -359,13 +360,14 @@ internal class SendViewModel @Inject constructor(
stateRouter.showSend()
}
transactionId != null && amount != null && destinationAddress != null -> {
+ loadFee()
uiState = stateFactory.getReadyState(amount, destinationAddress, memo)
- stateRouter.showFee()
+ stateRouter.showSend()
updateNotifications()
}
else -> {
- getWalletsAndRecent()
uiState = stateFactory.getReadyState()
+ getWalletsAndRecent()
stateRouter.showRecipient()
updateNotifications()
}
@@ -406,7 +408,8 @@ internal class SendViewModel @Inject constructor(
return if (!isMultiCurrency) {
val status = getCryptoCurrencyStatusSyncUseCase(walletId).getOrNull()
val address = status?.value?.networkAddress.takeIf {
- status?.currency?.network?.id == cryptoCurrency.network.id
+ status?.currency?.network?.id == cryptoCurrency.network.id &&
+ status.currency.network.derivationPath !is Network.DerivationPath.Custom
}
address?.let {
AvailableWallet(
@@ -417,7 +420,8 @@ internal class SendViewModel @Inject constructor(
} else {
val statuses = getCryptoCurrencyStatusesSyncUseCase(walletId).getOrNull()
val walletCurrency = statuses?.firstOrNull {
- it.currency.network.id == cryptoCurrency.network.id
+ it.currency.network.id == cryptoCurrency.network.id &&
+ it.currency.network.derivationPath !is Network.DerivationPath.Custom
}
val address = walletCurrency?.value?.networkAddress
address?.let {
@@ -522,8 +526,9 @@ internal class SendViewModel @Inject constructor(
)
return true
}
+ val feeSelectorState = uiState.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return false
return checkIfFeeTooHigh(
- state = uiState,
+ feeSelectorState = feeSelectorState,
onShow = { diff ->
uiState = eventStateFactory.getFeeTooHighAlert(
diff = diff,
@@ -702,6 +707,7 @@ internal class SendViewModel @Inject constructor(
if (result == null) {
onFeeLoadFailed(isShowStatus, isToNextState)
}
+ updateNotifications()
updateFeeNotifications()
}.saveIn(feeJobHolder)
.invokeOnCompletion {
@@ -735,7 +741,7 @@ internal class SendViewModel @Inject constructor(
return getFeeUseCase.invoke(
amount = amount,
destination = recipientState.addressTextField.value,
- userWalletId = userWalletId,
+ userWallet = userWallet,
cryptoCurrency = cryptoCurrency,
)
}
@@ -792,10 +798,11 @@ internal class SendViewModel @Inject constructor(
override fun onAmountReduceClick(reducedAmount: BigDecimal, clazz: Class) {
uiState = amountStateFactory.getOnAmountValueChange(reducedAmount.parseBigDecimal(cryptoCurrency.decimals))
uiState = sendNotificationFactory.dismissNotificationState(clazz)
+ updateNotifications()
}
override fun onNotificationCancel(clazz: Class) {
- uiState = sendNotificationFactory.dismissNotificationState(clazz)
+ uiState = sendNotificationFactory.dismissNotificationState(clazz = clazz, isIgnored = true)
}
private fun verifyAndSendTransaction() {
diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt
index 196d529e7b..afb2f253a4 100644
--- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt
+++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt
@@ -126,14 +126,15 @@ class SwapDomainModule {
@Provides
@Singleton
fun provideSendTransactionUseCase(
- @SwapScope isDemoCardUseCase: IsDemoCardUseCase,
cardSdkConfigRepository: CardSdkConfigRepository,
transactionRepository: TransactionRepository,
+ walletManagersFacade: WalletManagersFacade,
): SendTransactionUseCase {
return SendTransactionUseCase(
- isDemoCardUseCase = isDemoCardUseCase,
+ demoConfig = DemoConfig(),
cardSdkConfigRepository = cardSdkConfigRepository,
transactionRepository = transactionRepository,
+ walletManagersFacade = walletManagersFacade,
)
}
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 eaf4ddfa6b..d8edc6401c 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
@@ -646,6 +646,7 @@ internal class StateBuilder(
}
private fun getWarningForError(dataError: DataError, fromToken: CryptoCurrency): SwapWarning {
+ val providerErrorMessage = getProviderErrorMessage(dataError)
return when (dataError) {
is DataError.ExchangeTooSmallAmountError -> SwapWarning.GeneralError(
notificationConfig = NotificationConfig(
@@ -674,10 +675,16 @@ internal class StateBuilder(
} else {
resourceReference(R.string.warning_express_refresh_required_title)
},
- subtitle = if (dataError is DataError.UnknownError) {
- resourceReference(R.string.common_unknown_error)
- } else {
- resourceReference(R.string.express_error_code, wrappedList(dataError.code.toString()))
+ subtitle = when {
+ dataError is DataError.UnknownError -> {
+ resourceReference(R.string.common_unknown_error)
+ }
+ providerErrorMessage != null -> {
+ providerErrorMessage
+ }
+ else -> {
+ resourceReference(R.string.express_error_code, wrappedList(dataError.code.toString()))
+ }
},
iconResId = R.drawable.img_attention_20,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
@@ -972,7 +979,7 @@ internal class StateBuilder(
return uiState.copy(
alert = SwapWarning.GenericWarning(
message = if (swapTransactionState is SwapTransactionState.ExpressError) {
- getAlertErrorMessage(swapTransactionState.dataError)
+ getProviderErrorMessage(swapTransactionState.dataError)
} else {
null
},
@@ -987,7 +994,7 @@ internal class StateBuilder(
)
}
- private fun getAlertErrorMessage(dataError: DataError): TextReference? {
+ private fun getProviderErrorMessage(dataError: DataError): TextReference? {
return when (dataError) {
is DataError.SwapsAreUnavailableNowError -> resourceReference(
id = R.string.express_error_swap_unavailable,
@@ -998,6 +1005,7 @@ internal class StateBuilder(
formatArgs = wrappedList(dataError.code),
)
is DataError.ExchangeProviderNotActiveError,
+ is DataError.ExchangeProviderNotFoundError,
is DataError.ExchangeProviderNotAvailableError,
is DataError.ExchangeProviderProviderInternalError,
-> resourceReference(
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 d5445e3d1a..55e6cba9b0 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
@@ -4,7 +4,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.utils.BigDecimalFormatter
-import com.tangem.core.ui.utils.toDateFormat
+import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrency
@@ -71,7 +71,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
txUrl = transaction.status?.txExternalUrl,
txExternalId = transaction.status?.txExternalId,
timestamp = TextReference.Str(
- "${timestamp.toDateFormat()}, ${timestamp.toTimeFormat()}",
+ "${timestamp.toDateFormatWithTodayYesterday()}, ${timestamp.toTimeFormat()}",
),
fiatSymbol = appCurrency.symbol,
statuses = getStatuses(transaction.status?.status),
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryItemFlowConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryItemFlowConverter.kt
index 9fb6df70b1..6a8bc163c7 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryItemFlowConverter.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryItemFlowConverter.kt
@@ -4,7 +4,7 @@ import androidx.paging.*
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState
-import com.tangem.core.ui.utils.toDateFormat
+import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
@@ -68,7 +68,7 @@ internal class TokenDetailsTxHistoryItemFlowConverter(
// Use raw timestamp to get date
// If [afterDate] is the first transaction in the flow, add the group title
- val afterDate = after.getTimestamp()?.toDateFormat() ?: return@insertSeparators null
+ val afterDate = after.getTimestamp()?.toDateFormatWithTodayYesterday() ?: return@insertSeparators null
if (before is TxHistoryItemState.Title) {
return@insertSeparators TxHistoryItemState.GroupTitle(
title = afterDate,
@@ -80,7 +80,7 @@ internal class TokenDetailsTxHistoryItemFlowConverter(
* If [beforeDate] is not equals to [afterDate], then [afterDate] is first transaction in
* the new group
*/
- val beforeDate = before.getTimestamp()?.toDateFormat() ?: return@insertSeparators null
+ val beforeDate = before.getTimestamp()?.toDateFormatWithTodayYesterday() ?: return@insertSeparators null
return@insertSeparators if (beforeDate != afterDate) {
TxHistoryItemState.GroupTitle(
title = afterDate,
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 f4c38aca5c..02defbde3a 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
@@ -10,7 +10,6 @@ import com.tangem.blockchain.common.address.AddressType
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
-import com.tangem.core.deeplink.global.SellCurrencyDeepLink
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.extensions.resourceReference
@@ -165,7 +164,6 @@ internal class TokenDetailsViewModel @Inject constructor(
viewModel = this,
deepLinks = listOf(
BuyCurrencyDeepLink(::onBuyCurrencyDeepLink),
- SellCurrencyDeepLink(::onSellCurrencyDeepLink),
),
)
}
@@ -175,20 +173,6 @@ internal class TokenDetailsViewModel @Inject constructor(
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.Bought(currency.symbol))
}
- private fun onSellCurrencyDeepLink(data: SellCurrencyDeepLink.Data) {
- sendCurrency(
- status = cryptoCurrencyStatus ?: return,
- transactionInfo = data.let {
- TransactionInfo(
- transactionId = it.transactionId,
- destinationAddress = it.depositWalletAddress,
- amount = it.baseCurrencyAmount,
- tag = it.depositWalletAddressTag,
- )
- },
- )
- }
-
override fun onCreate(owner: LifecycleOwner) {
analyticsEventsHandler.send(
event = TokenScreenAnalyticsEvent.DetailsScreenOpened(token = cryptoCurrency.symbol),
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/deeplink/WalletDeepLinksHandler.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/deeplink/WalletDeepLinksHandler.kt
index 59e2022b58..3c1e89fbeb 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/deeplink/WalletDeepLinksHandler.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/deeplink/WalletDeepLinksHandler.kt
@@ -8,16 +8,14 @@ import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
import com.tangem.core.deeplink.global.SellCurrencyDeepLink
import com.tangem.domain.redux.ReduxStateHolder
-import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
-import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
-import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
-import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
+import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent
import com.tangem.domain.wallets.models.UserWallet
+import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
@@ -29,23 +27,20 @@ internal class WalletDeepLinksHandler @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
private val getCryptoCurrencyStatusSyncUseCase: GetCryptoCurrencyStatusSyncUseCase,
+ private val getCryptoCurrencyStatusesSyncUseCase: GetCryptoCurrencyStatusesSyncUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
private val reduxStateHolder: ReduxStateHolder,
) {
- private var deepLinks: List = emptyList()
+ private var deepLinksMap = mutableMapOf>()
- fun registerForSingleCurrencyWallets(viewModel: ViewModel, userWallet: UserWallet) {
- if (userWallet.isMultiCurrency) {
- deepLinksRegistry.unregister(deepLinks)
- } else {
- if (deepLinks.isEmpty()) {
- deepLinks = getDeepLinks(userWallet, viewModel.viewModelScope)
- }
-
- deepLinksRegistry.register(deepLinks)
+ fun registerForWallet(viewModel: ViewModel, userWallet: UserWallet) {
+ val deepLinks = deepLinksMap.getOrPut(userWallet.walletId) {
+ getDeepLinks(userWallet, viewModel.viewModelScope)
}
+ deepLinksRegistry.unregisterByIds(deepLinks.map { it.id })
+ deepLinksRegistry.register(deepLinks)
viewModel.addCloseable {
deepLinksRegistry.unregister(deepLinks)
@@ -60,20 +55,23 @@ internal class WalletDeepLinksHandler @Inject constructor(
}
},
)
- val buyCurrencyDeepLink = BuyCurrencyDeepLink(
- onReceive = {
- scope.launch {
- onBuyCurrencyDeepLink(userWallet)
- }
- },
- )
- return listOf(sellCurrencyDeepLink, buyCurrencyDeepLink)
+ return buildList {
+ add(sellCurrencyDeepLink)
+ if (!userWallet.isMultiCurrency) {
+ add(
+ BuyCurrencyDeepLink(
+ onReceive = {
+ scope.launch { onBuyCurrencyDeepLink(userWallet) }
+ },
+ ),
+ )
+ }
+ }
}
private suspend fun onSellCurrencyDeepLink(userWallet: UserWallet, data: SellCurrencyDeepLink.Data) {
- val cryptoCurrencyStatus = getCryptoCurrencyStatusSyncUseCase(userWallet.walletId)
- .getOrNull() ?: return
+ val cryptoCurrencyStatus = findCryptoCurrencyStatus(userWallet, data.currencyId) ?: return
val feeCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(
userWallet.walletId,
cryptoCurrencyStatus,
@@ -110,6 +108,19 @@ internal class WalletDeepLinksHandler @Inject constructor(
analyticsEventHandler.send(TokenScreenAnalyticsEvent.Bought(cryptoCurrency.symbol))
}
+ private suspend fun findCryptoCurrencyStatus(
+ userWallet: UserWallet,
+ currencyIdValue: String,
+ ): CryptoCurrencyStatus? {
+ return if (userWallet.isMultiCurrency) {
+ getCryptoCurrencyStatusesSyncUseCase(userWallet.walletId).getOrNull()?.let { currencies ->
+ currencies.find { currencyIdValue == it.currency.id.value }
+ }
+ } else {
+ getCryptoCurrencyStatusSyncUseCase(userWallet.walletId).getOrNull()
+ }
+ }
+
private fun sendCoin(
userWallet: UserWallet,
cryptoCurrencyStatus: CryptoCurrencyStatus,
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemFlowConverter.kt
index 62c49d5586..b24e902bfa 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemFlowConverter.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemFlowConverter.kt
@@ -4,7 +4,7 @@ import androidx.paging.*
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState
-import com.tangem.core.ui.utils.toDateFormat
+import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.utils.converter.Converter
import kotlinx.coroutines.CoroutineScope
@@ -50,7 +50,7 @@ internal class TxHistoryItemFlowConverter(
// Use raw timestamp to get date
// If [afterDate] is the first transaction in the flow, add the group title
- val afterDate = after.getTimestamp()?.toDateFormat() ?: return@insertSeparators null
+ val afterDate = after.getTimestamp()?.toDateFormatWithTodayYesterday() ?: return@insertSeparators null
if (before is TxHistoryItemState.Title) {
return@insertSeparators TxHistoryItemState.GroupTitle(afterDate, itemKey = UUID.randomUUID().toString())
}
@@ -59,7 +59,7 @@ internal class TxHistoryItemFlowConverter(
* If [beforeDate] is not equals to [afterDate], then [afterDate] is first transaction in
* the new group
*/
- val beforeDate = before.getTimestamp()?.toDateFormat() ?: return@insertSeparators null
+ val beforeDate = before.getTimestamp()?.toDateFormatWithTodayYesterday() ?: return@insertSeparators null
return@insertSeparators if (beforeDate != afterDate) {
TxHistoryItemState.GroupTitle(afterDate, itemKey = UUID.randomUUID().toString())
} else {
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt
index 63db20714d..369ec32691 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt
@@ -154,7 +154,7 @@ internal class WalletViewModel @Inject constructor(
selectedWalletAnalyticsSender.send(selectedWallet)
}
- walletDeepLinksHandler.registerForSingleCurrencyWallets(
+ walletDeepLinksHandler.registerForWallet(
viewModel = this,
userWallet = selectedWallet,
)
diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml
index 56d6351be9..eec9a20c7b 100644
--- a/gradle/dependencies.toml
+++ b/gradle/dependencies.toml
@@ -85,7 +85,7 @@ leakcanary = "2.13"
# endregion Other libraries
# region Tangem
-tangemBlockchainSdk = "release-app_5.9-600"
+tangemBlockchainSdk = "release-app_5.9-605"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "release-app_5.9-343"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^