Updated on 2026-08-14

This commit is contained in:
Tangem 2024-04-15 16:48:17 +05:00
commit f7fb77fa70
10 changed files with 57 additions and 28 deletions

View file

@ -9,6 +9,7 @@ import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.txhistory.repository.paging.TxHistoryPagingSource
import com.tangem.datasource.local.txhistory.TxHistoryItemsStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.txhistory.models.Page
@ -69,6 +70,7 @@ class DefaultTxHistoryRepository(
return pager.flow
}
@Deprecated("Replace with getTxExploreUrl [UserWalletId, Network] instead")
override fun getTxExploreUrl(txHash: String, networkId: Network.ID): String {
val blockchain = Blockchain.fromId(networkId.value)
return when (val txExploreState = blockchain.getExploreTxUrl(txHash)) {
@ -77,6 +79,19 @@ class DefaultTxHistoryRepository(
}
}
override suspend fun getTxExploreUrl(userWalletId: UserWalletId, network: Network): String {
val blockchain = Blockchain.fromNetworkId(network.id.value)
val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWalletId,
network = network,
)
val lastTxHash = walletManager?.wallet?.recentTransactions?.last()?.hash.orEmpty()
return when (val txExploreState = blockchain?.getExploreTxUrl(lastTxHash)) {
is TxExploreState.Url -> txExploreState.url
else -> ""
}
}
override suspend fun getFixedSizeTxHistoryItems(
userWalletId: UserWalletId,
currency: CryptoCurrency,

View file

@ -24,6 +24,9 @@ interface TxHistoryRepository {
fun getTxExploreUrl(txHash: String, networkId: Network.ID): String
/** Get transaction url in explorer via last transaction from wallet's recentTransactions list */
suspend fun getTxExploreUrl(userWalletId: UserWalletId, network: Network): String
@Throws(TxHistoryListError::class)
suspend fun getFixedSizeTxHistoryItems(
userWalletId: UserWalletId,

View file

@ -6,10 +6,12 @@ import arrow.core.raise.either
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.txhistory.models.TxStatusError
import com.tangem.domain.txhistory.repository.TxHistoryRepository
import com.tangem.domain.wallets.models.UserWalletId
class GetExplorerTransactionUrlUseCase(
private val repository: TxHistoryRepository,
) {
@Deprecated("Replace with invoke [UserWalletId, Network]")
operator fun invoke(txHash: String, networkId: Network.ID): Either<TxStatusError, String> {
return either {
catch(
@ -22,4 +24,17 @@ class GetExplorerTransactionUrlUseCase(
)
}
}
suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either<TxStatusError, String> {
return either {
catch(
block = {
repository.getTxExploreUrl(userWalletId, network).ifEmpty {
raise(TxStatusError.EmptyUrlError)
}
},
catch = { raise(TxStatusError.DataError(it)) },
)
}
}
}

View file

@ -8,7 +8,6 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.ValidateWalletMemoUseCase
import com.tangem.features.send.impl.R
@ -36,7 +35,6 @@ internal class SendStateFactory(
private val feeCryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
private val isTapHelpPreviewEnabledProvider: Provider<Boolean>,
private val validateWalletMemoUseCase: ValidateWalletMemoUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
) {
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
@ -92,7 +90,7 @@ internal class SendStateFactory(
event = consumedEvent(),
isEditingDisabled = false,
isBalanceHidden = false,
cryptoCurrencySymbol = "",
cryptoCurrencyName = "",
)
fun getReadyState(): SendUiState {
@ -103,7 +101,7 @@ internal class SendStateFactory(
?: recipientStateConverter.convert(SendRecipientStateConverter.Data("", null)),
feeState = state.feeState ?: feeStateConverter.convert(Unit),
sendState = confirmStateConverter.convert(Unit),
cryptoCurrencySymbol = cryptoCurrencyStatusProvider().currency.symbol,
cryptoCurrencyName = cryptoCurrencyStatusProvider().currency.name,
)
}
@ -115,7 +113,7 @@ internal class SendStateFactory(
?: recipientStateConverter.convert(SendRecipientStateConverter.Data(destinationAddress, memo)),
feeState = state.feeState ?: feeStateConverter.convert(Unit),
isEditingDisabled = true,
cryptoCurrencySymbol = cryptoCurrencyStatusProvider().currency.symbol,
cryptoCurrencyName = cryptoCurrencyStatusProvider().currency.name,
)
}
@ -251,14 +249,9 @@ internal class SendStateFactory(
)
}
fun getTransactionSendState(txData: TransactionData): SendUiState {
fun getTransactionSendState(txData: TransactionData, txUrl: String): SendUiState {
val state = currentStateProvider()
val cryptoCurrency = cryptoCurrencyStatusProvider().currency
val sendState = state.sendState ?: return state
val txUrl = getExplorerTransactionUrlUseCase(
txHash = txData.hash.orEmpty(),
networkId = cryptoCurrency.network.id,
).getOrElse { "" }
return state.copy(
sendState = sendState.copy(
transactionDate = txData.date?.timeInMillis ?: System.currentTimeMillis(),

View file

@ -23,7 +23,7 @@ import java.math.BigDecimal
internal data class SendUiState(
val clickIntents: SendClickIntents,
val isEditingDisabled: Boolean,
val cryptoCurrencySymbol: String,
val cryptoCurrencyName: String,
val amountState: SendStates.AmountState? = null,
val recipientState: SendStates.RecipientState? = null,
val feeState: SendStates.FeeState? = null,

View file

@ -8,6 +8,7 @@ import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.common.extensions.minimalAmount
import com.tangem.domain.tokens.GetBalanceNotEnoughForFeeWarningUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -225,13 +226,14 @@ internal class SendNotificationFactory(
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
val isTezos = cryptoCurrencyStatus.currency.network.id.value == Blockchain.Tezos.id
val threshold = Blockchain.Tezos.minimalAmount()
val isTotalBalance = feeAmount.plus(sendAmount) >= balance
if (!ignoreAmountReduce && isTotalBalance && isTezos) {
add(
SendNotification.Warning.HighFeeError(
amount = TEZOS_FEE_THRESHOLD.toPlainString(),
amount = threshold.toPlainString(),
onConfirmClick = {
val reduceTo = sendAmount.minus(TEZOS_FEE_THRESHOLD).toPlainString()
val reduceTo = sendAmount.minus(threshold).toPlainString()
clickIntents.onAmountReduceClick(reduceTo, SendNotification.Warning.HighFeeError::class.java)
},
onCloseClick = {
@ -372,7 +374,6 @@ internal class SendNotificationFactory(
companion object {
private const val DOGECOIN_MINIMUM = "0.01"
private val TEZOS_FEE_THRESHOLD = BigDecimal("0.01")
internal val FEE_MAX_DIFF = BigInteger("5")
}
}

View file

@ -43,7 +43,7 @@ internal fun SendScreen(uiState: SendUiState, currentState: SendUiCurrentScreen)
SendUiStateType.Recipient -> resourceReference(R.string.send_recipient_label)
SendUiStateType.Fee -> resourceReference(R.string.common_fee_selector_title)
SendUiStateType.Send -> if (!sendState.isSuccess) {
resourceReference(R.string.send_summary_title, wrappedList(uiState.cryptoCurrencySymbol))
resourceReference(R.string.send_summary_title, wrappedList(uiState.cryptoCurrencyName))
} else {
null
}

View file

@ -35,11 +35,7 @@ internal fun SendContent(uiState: SendUiState) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing16,
bottom = TangemTheme.dimens.spacing16,
),
.padding(horizontal = TangemTheme.dimens.spacing16),
) {
blocks(uiState)
tapHelp(isDisplay = sendState.showTapHelp)

View file

@ -89,9 +89,9 @@ internal class SendViewModel @Inject constructor(
private val parseQrCodeUseCase: ParseQrCodeUseCase,
private val isSendTapHelpEnabledUseCase: IsSendTapHelpEnabledUseCase,
private val neverShowTapHelpUseCase: NeverShowTapHelpUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
currencyChecksRepository: CurrencyChecksRepository,
isFeeApproximateUseCase: IsFeeApproximateUseCase,
getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
validateWalletMemoUseCase: ValidateWalletMemoUseCase,
getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase,
savedStateHandle: SavedStateHandle,
@ -123,7 +123,6 @@ internal class SendViewModel @Inject constructor(
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
feeCryptoCurrencyStatusProvider = Provider { feeCryptoCurrencyStatus },
validateWalletMemoUseCase = validateWalletMemoUseCase,
getExplorerTransactionUrlUseCase = getExplorerTransactionUrlUseCase,
isTapHelpPreviewEnabledProvider = Provider { isTapHelpPreviewEnabled },
)
@ -647,8 +646,7 @@ internal class SendViewModel @Inject constructor(
private fun autoNextFromRecipient(type: EnterAddressSource?, isValidAddress: Boolean) {
val isRecent = type == EnterAddressSource.RecentAddress
val isAddressOnly = uiState.recipientState?.memoTextField == null
if (isRecent && isAddressOnly && isValidAddress) onNextClick()
if (isRecent && isValidAddress) onNextClick()
}
// endregion
@ -846,13 +844,21 @@ internal class SendViewModel @Inject constructor(
},
ifRight = {
uiState = stateFactory.getSendingStateUpdate(isSending = false)
uiState = stateFactory.getTransactionSendState(txData)
updateTransactionStatus(txData)
scheduleBalanceUpdate()
analyticsEventHandler.send(SendAnalyticEvents.TransactionScreenOpened)
},
)
}
private suspend fun updateTransactionStatus(txData: TransactionData) {
val txUrl = getExplorerTransactionUrlUseCase(
userWalletId = userWalletId,
network = cryptoCurrency.network,
).getOrElse { "" }
uiState = stateFactory.getTransactionSendState(txData, txUrl)
}
private fun scheduleBalanceUpdate() {
viewModelScope.launch(dispatchers.io) {
delay(BALANCE_UPDATE_DELAY)

View file

@ -10,6 +10,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.extenstions.unwrap
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.common.extensions.minimalAmount
import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -433,7 +434,7 @@ internal class SwapInteractorImpl @Inject constructor(
) {
val isTezos = fromTokenStatus.currency.network.id.value == Blockchain.Tezos.id
if (isTezos && amount.value == fromTokenStatus.value.amount) {
warnings.add(Warning.ReduceAmountWarning(TEZOS_FEE_THRESHOLD))
warnings.add(Warning.ReduceAmountWarning(Blockchain.Tezos.minimalAmount()))
}
}
@ -1595,6 +1596,5 @@ internal class SwapInteractorImpl @Inject constructor(
private const val INCREASE_GAS_LIMIT_BY = 112 // 12%
private const val INCREASE_GAS_LIMIT_FOR_SEND = 105 // 5%
private const val INFINITY_SYMBOL = ""
private val TEZOS_FEE_THRESHOLD = BigDecimal("0.01")
}
}