Updated on 2026-08-14

This commit is contained in:
Tangem 2024-12-11 13:14:38 +05:00
parent 466b6e578b
commit df4288e70a
27 changed files with 381 additions and 31 deletions

View file

@ -58,6 +58,28 @@ internal object TransactionDomainModule {
)
}
@Provides
@Singleton
fun provideRetryTransactionUseCase(
cardSdkConfigRepository: CardSdkConfigRepository,
walletManagersFacade: WalletManagersFacade,
): RetryIncompleteTransactionUseCase {
return RetryIncompleteTransactionUseCase(
cardSdkConfigRepository = cardSdkConfigRepository,
walletManagersFacade = walletManagersFacade,
)
}
@Provides
@Singleton
fun provideDismissIncompleteTransactionUseCase(
walletManagersFacade: WalletManagersFacade,
): DismissIncompleteTransactionUseCase {
return DismissIncompleteTransactionUseCase(
walletManagersFacade = walletManagersFacade,
)
}
@Provides
@Singleton
fun provideCreateTransactionUseCase(transactionRepository: TransactionRepository): CreateTransactionUseCase {

View file

@ -185,7 +185,7 @@ object NotificationsFactory {
if (dustValue == null) return
val isExceedsLimit = checkDustLimits(
feeAmount = feeValue,
receivedAmount = sendingAmount,
sendingAmount = sendingAmount,
dustValue = dustValue,
cryptoCurrencyStatus = cryptoCurrencyStatus,
feeCurrencyStatus = feeCurrencyStatus,
@ -352,7 +352,7 @@ object NotificationsFactory {
private fun checkDustLimits(
feeAmount: BigDecimal,
receivedAmount: BigDecimal,
sendingAmount: BigDecimal,
dustValue: BigDecimal,
cryptoCurrencyStatus: CryptoCurrencyStatus,
feeCurrencyStatus: CryptoCurrencyStatus?,
@ -360,7 +360,7 @@ object NotificationsFactory {
val change = when (cryptoCurrencyStatus.currency) {
is CryptoCurrency.Coin -> {
val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
balance - (feeAmount + receivedAmount)
balance - (feeAmount + sendingAmount)
}
is CryptoCurrency.Token -> {
val balance = feeCurrencyStatus?.value?.amount ?: BigDecimal.ZERO
@ -369,6 +369,9 @@ object NotificationsFactory {
}
val isChangeLowerThanDust = change < dustValue && change > BigDecimal.ZERO
return receivedAmount < dustValue || isChangeLowerThanDust
return when (cryptoCurrencyStatus.currency) {
is CryptoCurrency.Coin -> sendingAmount < dustValue || isChangeLowerThanDust
is CryptoCurrency.Token -> isChangeLowerThanDust
}
}
}

View file

@ -222,13 +222,23 @@ private fun Buttons(state: NotificationButtonsState?, isEnabled: Boolean = true)
@Composable
private fun SingleSecondaryButton(config: NotificationButtonsState.SecondaryButtonConfig, isEnabled: Boolean = true) {
SecondaryButton(
text = config.text.resolveReference(),
onClick = config.onClick,
modifier = Modifier.fillMaxWidth(),
size = TangemButtonSize.WideAction,
enabled = isEnabled,
)
if (config.iconResId != null) {
SecondaryButtonIconEnd(
text = config.text.resolveReference(),
onClick = config.onClick,
modifier = Modifier.fillMaxWidth(),
iconResId = config.iconResId,
enabled = isEnabled,
)
} else {
SecondaryButton(
text = config.text.resolveReference(),
onClick = config.onClick,
modifier = Modifier.fillMaxWidth(),
size = TangemButtonSize.WideAction,
enabled = isEnabled,
)
}
}
@Composable

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M10.833,10.833H9.167V5.833H10.833M10.833,14.167H9.167V12.5H10.833M10,1.667C8.906,1.667 7.822,1.882 6.811,2.301C5.8,2.72 4.881,3.334 4.107,4.108C2.545,5.67 1.667,7.79 1.667,10C1.667,12.21 2.545,14.33 4.107,15.893C4.881,16.667 5.8,17.28 6.811,17.699C7.822,18.118 8.906,18.333 10,18.333C12.21,18.333 14.33,17.455 15.892,15.893C17.455,14.33 18.333,12.21 18.333,10C18.333,8.906 18.118,7.822 17.699,6.811C17.28,5.8 16.666,4.881 15.892,4.108C15.119,3.334 14.2,2.72 13.189,2.301C12.178,1.882 11.094,1.667 10,1.667Z"
android:fillColor="#FF3333"/>
</vector>

View file

@ -52,6 +52,7 @@ internal class DefaultCustomTokensRepository(
Blockchain.Unknown,
Blockchain.Binance,
Blockchain.BinanceTestnet,
Blockchain.Kaspa,
-> true
Blockchain.Cardano -> blockchain.validateContractAddress(contractAddress)
else -> blockchain.validateAddress(contractAddress)

View file

@ -7,6 +7,7 @@ import com.tangem.blockchain.common.ReserveAmountProvider
import com.tangem.blockchain.common.UtxoAmountLimitProvider
import com.tangem.data.tokens.converters.UtxoConverter
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.CurrencyAmount
import com.tangem.domain.tokens.model.Network
@ -24,6 +25,7 @@ internal class DefaultCurrencyChecksRepository(
private val walletManagersFacade: WalletManagersFacade,
private val coroutineDispatchers: CoroutineDispatcherProvider,
) : CurrencyChecksRepository {
override suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal? {
val manager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWalletId,
@ -108,6 +110,7 @@ internal class DefaultCurrencyChecksRepository(
override suspend fun checkUtxoAmountLimit(
userWalletId: UserWalletId,
network: Network,
currency: CryptoCurrency,
amount: BigDecimal,
fee: BigDecimal,
): UtxoAmountLimit? {

View file

@ -594,7 +594,7 @@ class DefaultWalletManagersFacade(
}
}
override suspend fun associateAsset(
override suspend fun fulfillRequirements(
userWalletId: UserWalletId,
currency: CryptoCurrency,
signer: TransactionSigner,
@ -613,6 +613,21 @@ class DefaultWalletManagersFacade(
}
}
override suspend fun discardRequirements(userWalletId: UserWalletId, currency: CryptoCurrency): SimpleResult {
return withContext(dispatchers.io) {
val walletManager = getOrCreateWalletManager(userWalletId = userWalletId, network = currency.network)
val currencyType = cryptoCurrencyTypeConverter.convert(currency)
if (walletManager !is AssetRequirementsManager) {
return@withContext SimpleResult.Failure(
BlockchainSdkError.CustomError("WalletManager is not implemented AssetRequirementsManager"),
)
}
walletManager.discardRequirements(currencyType)
}
}
override suspend fun checkUtxoConsolidationAvailability(userWalletId: UserWalletId, network: Network): Boolean {
val blockchain = Blockchain.fromId(network.id.value)
val walletManager = getOrCreateWalletManager(

View file

@ -233,12 +233,14 @@ interface WalletManagersFacade {
*/
suspend fun getAssetRequirements(userWalletId: UserWalletId, currency: CryptoCurrency): AssetRequirementsCondition?
suspend fun associateAsset(
suspend fun fulfillRequirements(
userWalletId: UserWalletId,
currency: CryptoCurrency,
signer: TransactionSigner,
): SimpleResult
suspend fun discardRequirements(userWalletId: UserWalletId, currency: CryptoCurrency): SimpleResult
/**
* Indicates UTXO consolidation availability
*

View file

@ -7,13 +7,20 @@ import com.tangem.blockchain.common.trustlines.AssetRequirementsCondition as Sdk
internal class SdkRequirementsConditionConverter : Converter<SdkRequirementsCondition, AssetRequirementsCondition> {
override fun convert(value: SdkRequirementsCondition): AssetRequirementsCondition {
return when (value) {
SdkRequirementsCondition.PaidTransaction -> AssetRequirementsCondition.PaidTransaction
is SdkRequirementsCondition.PaidTransaction -> AssetRequirementsCondition.PaidTransaction
is SdkRequirementsCondition.PaidTransactionWithFee -> AssetRequirementsCondition.PaidTransactionWithFee(
feeAmount = requireNotNull(value.feeAmount.value),
feeCurrencySymbol = value.feeAmount.currencySymbol,
decimals = value.feeAmount.decimals,
)
is SdkRequirementsCondition.IncompleteTransaction -> TODO()
is SdkRequirementsCondition.IncompleteTransaction -> AssetRequirementsCondition.IncompleteTransaction(
amount = requireNotNull(value.amount.value),
currencySymbol = value.amount.currencySymbol,
currencyDecimals = value.amount.decimals,
feeAmount = requireNotNull(value.feeAmount.value),
feeCurrencySymbol = value.feeAmount.currencySymbol,
feeCurrencyDecimals = value.feeAmount.decimals,
)
}
}
}

View file

@ -0,0 +1,16 @@
package com.tangem.domain.tokens.model.warnings
import com.tangem.domain.tokens.model.CryptoCurrency
import java.math.BigDecimal
sealed class KaspaWarnings : CryptoCurrencyWarning() {
abstract val currency: CryptoCurrency
data class IncompleteTransaction(
override val currency: CryptoCurrency,
val amount: BigDecimal,
val currencySymbol: String,
val currencyDecimals: Int,
) : KaspaWarnings()
}

View file

@ -1,5 +1,6 @@
package com.tangem.domain.tokens
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
@ -22,7 +23,8 @@ class GetCurrencyCheckUseCase(
recipientAddress: String? = null,
): CryptoCurrencyCheck {
return withContext(dispatchers.io) {
val network = currencyStatus.currency.network
val currency = currencyStatus.currency
val network = currency.network
val dustValue = currencyChecksRepository.getDustValue(userWalletId, network)
val reserveAmount = currencyChecksRepository.getReserveAmount(userWalletId, network)
val minimumSendAmount = currencyChecksRepository.getMinimumSendAmount(userWalletId, network)
@ -39,10 +41,11 @@ class GetCurrencyCheckUseCase(
recipientAddress,
)
} ?: false
val utxoAmountLimit = if (amount != null && fee != null) {
val utxoAmountLimit = if (currency is CryptoCurrency.Coin && amount != null && fee != null) {
currencyChecksRepository.checkUtxoAmountLimit(
userWalletId = userWalletId,
network = network,
currency = currencyStatus.currency,
amount = amount,
fee = fee,
)

View file

@ -6,6 +6,7 @@ import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.model.*
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.domain.tokens.model.warnings.HederaWarnings
import com.tangem.domain.tokens.model.warnings.KaspaWarnings
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
import com.tangem.domain.tokens.repository.*
import com.tangem.domain.transaction.models.AssetRequirementsCondition
@ -20,7 +21,7 @@ import com.tangem.utils.isNullOrZero
import kotlinx.coroutines.flow.*
import java.math.BigDecimal
@Suppress("LongParameterList")
@Suppress("LongParameterList", "LargeClass")
class GetCurrencyWarningsUseCase(
private val walletManagersFacade: WalletManagersFacade,
private val currenciesRepository: CurrenciesRepository,
@ -79,7 +80,17 @@ class GetCurrencyWarningsUseCase(
getAssetRequirementsWarning(userWalletId = userWalletId, currency = currency),
getMigrationFromMaticToPolWarning(currency),
)
}.flowOn(dispatchers.io)
}
.onEmpty {
setOfNotNull(
getNetworkUnavailableWarning(currencyStatus),
getNetworkNoAccountWarning(currencyStatus),
getBeaconChainShutdownWarning(currency.network.id),
getAssetRequirementsWarning(userWalletId = userWalletId, currency = currency),
getMigrationFromMaticToPolWarning(currency),
)
}
.flowOn(dispatchers.io)
}
private suspend fun getSwapPromoNotificationWarning(
@ -298,12 +309,22 @@ class GetCurrencyWarningsUseCase(
): CryptoCurrencyWarning? {
return when (val requirements = walletManagersFacade.getAssetRequirements(userWalletId, currency)) {
is AssetRequirementsCondition.PaidTransaction -> HederaWarnings.AssociateWarning(currency = currency)
is AssetRequirementsCondition.PaidTransactionWithFee -> HederaWarnings.AssociateWarningWithFee(
currency = currency,
fee = requirements.feeAmount,
feeCurrencySymbol = requirements.feeCurrencySymbol,
feeCurrencyDecimals = requirements.decimals,
)
is AssetRequirementsCondition.PaidTransactionWithFee -> {
HederaWarnings.AssociateWarningWithFee(
currency = currency,
fee = requirements.feeAmount,
feeCurrencySymbol = requirements.feeCurrencySymbol,
feeCurrencyDecimals = requirements.decimals,
)
}
is AssetRequirementsCondition.IncompleteTransaction ->
KaspaWarnings.IncompleteTransaction(
currency = currency,
amount = requirements.amount,
currencySymbol = requirements.currencySymbol,
currencyDecimals = requirements.currencyDecimals,
)
null -> null
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.domain.tokens.repository
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.CurrencyAmount
import com.tangem.domain.tokens.model.Network
@ -38,6 +39,7 @@ interface CurrencyChecksRepository {
suspend fun checkUtxoAmountLimit(
userWalletId: UserWalletId,
network: Network,
currency: CryptoCurrency,
amount: BigDecimal,
fee: BigDecimal,
): UtxoAmountLimit?

View file

@ -17,4 +17,16 @@ sealed class AssetRequirementsCondition {
val feeCurrencySymbol: String,
val decimals: Int,
) : AssetRequirementsCondition()
/**
* The exact value of the fee for this type of condition is stored in `feeAmount`.
*/
data class IncompleteTransaction(
val amount: BigDecimal,
val currencySymbol: String,
val currencyDecimals: Int,
val feeAmount: BigDecimal,
val feeCurrencySymbol: String,
val feeCurrencyDecimals: Int,
) : AssetRequirementsCondition()
}

View file

@ -0,0 +1,5 @@
package com.tangem.domain.transaction.error
sealed class IncompleteTransactionError {
data class DataError(val message: String?) : IncompleteTransactionError()
}

View file

@ -39,7 +39,7 @@ class AssociateAssetUseCase(
catch(
block = {
when (val result = walletManagersFacade.associateAsset(userWalletId, currency, signer)) {
when (val result = walletManagersFacade.fulfillRequirements(userWalletId, currency, signer)) {
is SimpleResult.Failure -> raise(AssociateAssetError.DataError(result.error.customMessage))
SimpleResult.Success -> Unit
}

View file

@ -0,0 +1,34 @@
package com.tangem.domain.transaction.usecase
import arrow.core.Either
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.transaction.error.IncompleteTransactionError
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
class DismissIncompleteTransactionUseCase(
private val walletManagersFacade: WalletManagersFacade,
) {
suspend operator fun invoke(
userWalletId: UserWalletId,
currency: CryptoCurrency,
): Either<IncompleteTransactionError, Unit> {
return either {
catch(
block = {
when (val result = walletManagersFacade.discardRequirements(userWalletId, currency)) {
is SimpleResult.Failure -> raise(
IncompleteTransactionError.DataError(result.error.customMessage),
)
SimpleResult.Success -> Unit
}
},
catch = { error -> IncompleteTransactionError.DataError(error.message) },
)
}
}
}

View file

@ -0,0 +1,38 @@
package com.tangem.domain.transaction.usecase
import arrow.core.Either
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.transaction.error.IncompleteTransactionError
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
class RetryIncompleteTransactionUseCase(
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val walletManagersFacade: WalletManagersFacade,
) {
suspend operator fun invoke(
userWalletId: UserWalletId,
currency: CryptoCurrency,
): Either<IncompleteTransactionError, Unit> {
return either {
val signer = cardSdkConfigRepository.getCommonSigner(cardId = null)
catch(
block = {
when (val result = walletManagersFacade.fulfillRequirements(userWalletId, currency, signer)) {
is SimpleResult.Failure -> raise(
IncompleteTransactionError.DataError(result.error.customMessage),
)
SimpleResult.Success -> Unit
}
},
catch = { error -> IncompleteTransactionError.DataError(error.message) },
)
}
}
}

View file

@ -48,6 +48,7 @@ internal class TokenDetailsNotificationsAnalyticsSender(
is TokenDetailsNotification.RentInfo,
is TokenDetailsNotification.NetworkShutdown,
is TokenDetailsNotification.HederaAssociateWarning,
is TokenDetailsNotification.KaspaIncompleteTransactionWarning,
is TokenDetailsNotification.KoinosMana,
is TokenDetailsNotification.MigrationMaticToPol,
-> null

View file

@ -96,6 +96,27 @@ internal data class TokenDetailsDialogConfig(
)
}
data class RemoveIncompleteTransactionConfirmDialogConfig(
val onConfirmClick: () -> Unit,
val onCancelClick: () -> Unit,
) : DialogContentConfig() {
override val title = null
override val message: TextReference = TextReference.Res(
id = R.string.warning_kaspa_unfinished_token_transaction_discard_message,
)
override val cancelButtonConfig: ButtonConfig = ButtonConfig(
text = TextReference.Res(R.string.common_cancel),
onClick = onCancelClick,
)
override val confirmButtonConfig: ButtonConfig = ButtonConfig(
text = TextReference.Res(R.string.common_yes),
onClick = onConfirmClick,
)
}
data class ErrorDialogConfig(
val text: TextReference,
val onConfirmClick: () -> Unit,

View file

@ -19,12 +19,14 @@ internal sealed class TokenDetailsNotification(val config: NotificationConfig) {
subtitle: TextReference,
iconResId: Int = R.drawable.img_attention_20,
buttonsState: NotificationConfig.ButtonsState? = null,
onCloseClick: (() -> Unit)? = null,
) : TokenDetailsNotification(
config = NotificationConfig(
title = title,
subtitle = subtitle,
iconResId = iconResId,
buttonsState = buttonsState,
onCloseClick = onCloseClick,
),
)
@ -191,6 +193,27 @@ internal sealed class TokenDetailsNotification(val config: NotificationConfig) {
),
)
data class KaspaIncompleteTransactionWarning(
private val currency: CryptoCurrency,
private val amount: String,
private val currencySymbol: String,
private val onRetryIncompleteTransactionClick: () -> Unit,
private val onDismissIncompleteTransactionClick: () -> Unit,
) : Warning(
title = resourceReference(R.string.warning_kaspa_unfinished_token_transaction_title),
subtitle = resourceReference(
id = R.string.warning_kaspa_unfinished_token_transaction_message,
formatArgs = wrappedList(amount, currencySymbol),
),
iconResId = R.drawable.ic_alert_circle_red_20,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(R.string.alert_button_try_again),
onClick = onRetryIncompleteTransactionClick,
iconResId = R.drawable.ic_tangem_24,
),
onCloseClick = onDismissIncompleteTransactionClick,
)
data class KoinosMana(
val manaBalanceAmount: String,
val maxManaBalanceAmount: String,

View file

@ -9,6 +9,7 @@ import com.tangem.core.ui.format.bigdecimal.shorted
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.domain.tokens.model.warnings.HederaWarnings
import com.tangem.domain.tokens.model.warnings.KaspaWarnings
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification.*
@ -41,6 +42,14 @@ internal class TokenDetailsNotificationConverter(
return newNotifications.toImmutableList()
}
fun removeKaspaIncompleteTransactionWarning(
currentState: TokenDetailsState,
): ImmutableList<TokenDetailsNotification> {
val newNotifications = currentState.notifications.toMutableList()
newNotifications.removeBy { it is KaspaIncompleteTransactionWarning }
return newNotifications.toImmutableList()
}
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun mapToNotification(warning: CryptoCurrencyWarning): TokenDetailsNotification {
return when (warning) {
@ -109,6 +118,13 @@ internal class TokenDetailsNotificationConverter(
feeCurrencySymbol = warning.feeCurrencySymbol,
onAssociateClick = clickIntents::onAssociateClick,
)
is KaspaWarnings.IncompleteTransaction -> KaspaIncompleteTransactionWarning(
currency = warning.currency,
amount = warning.amount.format { crypto(symbol = "", decimals = warning.currencyDecimals) },
currencySymbol = warning.currencySymbol,
onRetryIncompleteTransactionClick = clickIntents::onRetryIncompleteTransactionClick,
onDismissIncompleteTransactionClick = clickIntents::onDismissIncompleteTransactionClick,
)
is CryptoCurrencyWarning.FeeResourceInfo -> KoinosMana(
manaBalanceAmount = formatMana(warning.amount),
maxManaBalanceAmount = warning.maxAmount?.let {

View file

@ -199,6 +199,19 @@ internal class TokenDetailsStateFactory(
)
}
fun getStateWithDismissIncompleteTransactionConfirmDialog(): TokenDetailsState {
return currentStateProvider().copy(
dialogConfig = TokenDetailsDialogConfig(
isShow = true,
onDismissRequest = clickIntents::onDismissDialog,
content = TokenDetailsDialogConfig.DialogContentConfig.RemoveIncompleteTransactionConfirmDialogConfig(
onConfirmClick = clickIntents::onConfirmDismissIncompleteTransactionClick,
onCancelClick = clickIntents::onDismissDialog,
),
),
)
}
fun getStateWithActionButtonErrorDialog(unavailabilityReason: ScenarioUnavailabilityReason): TokenDetailsState {
return currentStateProvider().copy(
dialogConfig = TokenDetailsDialogConfig(
@ -300,6 +313,14 @@ internal class TokenDetailsStateFactory(
return state.copy(notifications = notificationConverter.removeHederaAssociateWarning(state))
}
fun getStateWithRemovedKaspaIncompleteTransactionNotification(): TokenDetailsState {
val state = currentStateProvider()
return state.copy(
notifications = notificationConverter.removeKaspaIncompleteTransactionWarning(state),
dialogConfig = state.dialogConfig?.copy(isShow = false),
)
}
fun getStateAndTriggerEvent(
state: TokenDetailsState,
errorMessage: TextReference,

View file

@ -59,6 +59,12 @@ interface TokenDetailsClickIntents {
fun onAssociateClick()
fun onRetryIncompleteTransactionClick()
fun onDismissIncompleteTransactionClick()
fun onConfirmDismissIncompleteTransactionClick()
fun onStakeBannerClick()
fun onBalanceSelect(config: TokenBalanceSegmentedButtonConfig)

View file

@ -50,7 +50,10 @@ import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText
import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent
import com.tangem.domain.transaction.error.AssociateAssetError
import com.tangem.domain.transaction.error.IncompleteTransactionError
import com.tangem.domain.transaction.usecase.AssociateAssetUseCase
import com.tangem.domain.transaction.usecase.DismissIncompleteTransactionUseCase
import com.tangem.domain.transaction.usecase.RetryIncompleteTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
@ -108,6 +111,8 @@ internal class TokenDetailsViewModel @Inject constructor(
private val networkHasDerivationUseCase: NetworkHasDerivationUseCase,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val associateAssetUseCase: AssociateAssetUseCase,
private val retryIncompleteTransactionUseCase: RetryIncompleteTransactionUseCase,
private val dismissIncompleteTransactionUseCase: DismissIncompleteTransactionUseCase,
private val reduxStateHolder: ReduxStateHolder,
private val analyticsEventsHandler: AnalyticsEventHandler,
private val vibratorHapticManager: VibratorHapticManager,
@ -861,6 +866,58 @@ internal class TokenDetailsViewModel @Inject constructor(
return resourceReference(R.string.wallet_notification_address_copied)
}
override fun onRetryIncompleteTransactionClick() {
viewModelScope.launch {
retryIncompleteTransactionUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
).fold(
ifLeft = { e ->
when (e) {
is IncompleteTransactionError.DataError -> {
internalUiState.value = stateFactory.getStateWithErrorDialog(
stringReference(e.message.orEmpty()),
)
Timber.e(e.message)
}
}
},
ifRight = {
internalUiState.value = stateFactory.getStateWithRemovedKaspaIncompleteTransactionNotification()
},
)
}
}
override fun onDismissIncompleteTransactionClick() {
viewModelScope.launch {
internalUiState.value = stateFactory.getStateWithDismissIncompleteTransactionConfirmDialog()
}
}
override fun onConfirmDismissIncompleteTransactionClick() {
viewModelScope.launch {
dismissIncompleteTransactionUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
).fold(
ifLeft = { e ->
when (e) {
is IncompleteTransactionError.DataError -> {
internalUiState.value = stateFactory.getStateWithErrorDialog(
stringReference(e.message.orEmpty()),
)
Timber.e(e.message)
}
}
},
ifRight = {
internalUiState.value = stateFactory.getStateWithRemovedKaspaIncompleteTransactionNotification()
},
)
}
}
override fun onAssociateClick() {
analyticsEventsHandler.send(
TokenScreenAnalyticsEvent.Associate(

View file

@ -88,7 +88,7 @@ markdownComposeView = "0.5.4"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "release-app_5.19-881"
tangemBlockchainSdk = "release-app_5.19-882"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "release-app_5.19-414"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^

View file

@ -21,13 +21,15 @@ internal class DefaultBlockchainDataStorage(
return appPreferencesStore.getSyncOrNull(key = stringPreferencesKey(name = key))
}
override suspend fun remove(key: String) {
TODO("Not yet implemented")
}
override suspend fun store(key: String, value: String) {
appPreferencesStore.edit {
it[stringPreferencesKey(key)] = value
}
}
override suspend fun remove(key: String) {
appPreferencesStore.edit {
it.remove(stringPreferencesKey(key))
}
}
}