Updated on 2026-08-14

This commit is contained in:
Tangem 2023-12-25 19:30:50 +03:00
parent 7e5473b8c5
commit 9ae8e54953
10 changed files with 70 additions and 29 deletions

View file

@ -48,6 +48,9 @@ enum class ExchangeStatus {
@Json(name = "verifying")
VERIFYING,
@Json(name = "cancelled")
CANCELLED,
}
data class ExchangeStatusError(

View file

@ -205,6 +205,7 @@
<string name="express_exchange_notification_failed_title">Операция не выполнена провайдером</string>
<string name="express_exchange_notification_verification_text">Посетите сайт провайдера для проверки</string>
<string name="express_exchange_notification_verification_title">Провайдер запрашивает прохождение верификации</string>
<string name="express_exchange_status_canceled">Отменен</string>
<string name="express_exchange_status_confirmed">Подтверждено</string>
<string name="express_exchange_status_confirming">Подтверждение</string>
<string name="express_exchange_status_confirming_active">Подтверждение...</string>

View file

@ -205,7 +205,7 @@
<string name="express_exchange_notification_failed_title">Operation failed by provider</string>
<string name="express_exchange_notification_verification_text">Visit providers website for verification</string>
<string name="express_exchange_notification_verification_title">KYC verification required by provider</string>
<string name="express_exchange_status_cancelled">Cancelled</string>
<string name="express_exchange_status_canceled">Canceled</string>
<string name="express_exchange_status_confirmed">Confirmed</string>
<string name="express_exchange_status_confirming">Confirming</string>
<string name="express_exchange_status_confirming_active">Confirming…</string>

View file

@ -17,4 +17,5 @@ enum class ExchangeStatus {
Sending,
Finished,
Refunded,
Cancelled,
}

View file

@ -29,6 +29,7 @@ internal data class SwapTransactionsState(
val fromCryptoSymbol: String,
val fromFiatAmount: String,
val fromCurrencyIcon: TokenIconState,
val showProviderLink: Boolean,
val onClick: () -> Unit,
val onGoToProviderClick: (String) -> Unit,
)

View file

@ -62,6 +62,8 @@ internal class TokenDetailsSwapTransactionsStateConverter(
val toFiatAmount = toAmount.multiply(toCurrency.value.fiatRate)
val fromFiatAmount = fromAmount.multiply(fromCurrency.value.fiatRate)
val timestamp = transaction.timestamp
val notifications = getNotification(transaction.status?.status, transaction.status?.txExternalUrl)
val showProviderLink = getShowProviderLink(notifications, transaction.status)
result.add(
SwapTransactionsState(
txId = transaction.txId,
@ -92,6 +94,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
fromCryptoSymbol = fromCurrency.currency.symbol,
fromFiatAmount = getFiatAmount(fromFiatAmount),
fromCurrencyIcon = iconStateConverter.convert(fromCurrency),
showProviderLink = showProviderLink,
onClick = { clickIntents.onSwapTransactionClick(transaction.txId) },
onGoToProviderClick = { url ->
analyticsEventsHandlerProvider().send(
@ -109,12 +112,15 @@ internal class TokenDetailsSwapTransactionsStateConverter(
fun updateTxStatus(tx: SwapTransactionsState, statusModel: ExchangeStatusModel?): SwapTransactionsState {
if (statusModel == null || tx.activeStatus == statusModel.status) return tx
val hasFailed = tx.hasFailed || statusModel.status == ExchangeStatus.Failed
val notifications = getNotification(statusModel.status, statusModel.txExternalUrl)
val showProviderLink = getShowProviderLink(notifications, statusModel)
return tx.copy(
activeStatus = statusModel.status,
hasFailed = hasFailed,
notification = getNotification(statusModel.status, statusModel.txExternalUrl),
notification = notifications,
statuses = getStatuses(statusModel.status, hasFailed),
txUrl = statusModel.txExternalUrl,
showProviderLink = showProviderLink,
)
}
@ -149,6 +155,9 @@ internal class TokenDetailsSwapTransactionsStateConverter(
}
}
private fun getShowProviderLink(notifications: ExchangeStatusNotifications?, status: ExchangeStatusModel?) =
notifications == null && status?.txExternalUrl != null && status.status != ExchangeStatus.Cancelled
private fun getStatuses(status: ExchangeStatus?, hasFailed: Boolean = false): ImmutableList<ExchangeStatusState> {
if (status == null) return persistentListOf()
val isWaiting = status == ExchangeStatus.New || status == ExchangeStatus.Waiting
@ -164,26 +173,37 @@ internal class TokenDetailsSwapTransactionsStateConverter(
val isExchangingDone = !isExchanging && isConfirmingDone
val isSendingDone = !isSending && !isVerifying && !isFailed && isExchangingDone
return listOf(
waitStep(isWaiting, isWaitingDone),
confirmStep(isConfirming, isConfirmingDone),
exchangeStep(
isExchanging = isExchanging,
isExchangingDone = isExchangingDone,
isRefunded = isRefunded,
hasFailed = hasFailed,
isVerifying = isVerifying,
isFailed = isFailed,
),
sendStep(
isSending = isSending,
isSendingDone = isSendingDone,
isRefunded = isRefunded,
hasFailed = hasFailed,
),
).toPersistentList()
return if (status == ExchangeStatus.Cancelled) {
listOf(cancelledStep())
} else {
listOf(
waitStep(isWaiting, isWaitingDone),
confirmStep(isConfirming, isConfirmingDone),
exchangeStep(
isExchanging = isExchanging,
isExchangingDone = isExchangingDone,
isRefunded = isRefunded,
hasFailed = hasFailed,
isVerifying = isVerifying,
isFailed = isFailed,
),
sendStep(
isSending = isSending,
isSendingDone = isSendingDone,
isRefunded = isRefunded,
hasFailed = hasFailed,
),
)
}.toPersistentList()
}
private fun cancelledStep() = ExchangeStatusState(
status = ExchangeStatus.Cancelled,
text = TextReference.Res(R.string.express_exchange_status_canceled),
isActive = true,
isDone = true,
)
private fun waitStep(isNew: Boolean, isNewDone: Boolean) = ExchangeStatusState(
status = ExchangeStatus.New,
text = when {

View file

@ -75,11 +75,15 @@ internal fun ExchangeStatusBlock(
}
}
statuses.forEachIndexed { index, item ->
ExchangeStatusStep(
stepStatus = item,
isLast = index == statuses.lastIndex,
)
AnimatedContent(targetState = statuses.lastIndex, label = "Exchange Status List Change") {
Column {
statuses.forEachIndexed { index, item ->
ExchangeStatusStep(
stepStatus = item,
isLast = index == it,
)
}
}
}
}
}
@ -101,6 +105,11 @@ private fun ExchangeStatusStep(
.size(TangemTheme.dimens.size20),
) {
when {
it.status == ExchangeStatus.Cancelled -> ExchangeStep(
iconRes = R.drawable.ic_close_24,
color = TangemTheme.colors.icon.warning,
isDone = false,
)
it.status == ExchangeStatus.Failed -> ExchangeStep(
iconRes = R.drawable.ic_close_24,
color = TangemTheme.colors.icon.warning,
@ -131,6 +140,7 @@ private fun ExchangeStatusStep(
@Composable
private fun ExchangeStatusStepText(stepStatus: ExchangeStatusState) {
val textColor = when {
stepStatus.status == ExchangeStatus.Cancelled -> TangemTheme.colors.icon.warning
stepStatus.status == ExchangeStatus.Failed && !stepStatus.isDone -> TangemTheme.colors.icon.warning
stepStatus.status == ExchangeStatus.Verifying && !stepStatus.isDone -> TangemTheme.colors.icon.attention
stepStatus.isDone -> TangemTheme.colors.text.primary1

View file

@ -76,7 +76,7 @@ private fun ExchangeStatusBottomSheetContent(content: ExchangeStatusBottomSheetC
SpacerH12()
ExchangeStatusBlock(
statuses = config.statuses,
showLink = config.notification == null && config.txUrl != null,
showLink = config.showProviderLink,
onClick = { config.onGoToProviderClick(config.txUrl.orEmpty()) },
)
AnimatedContent(

View file

@ -4,7 +4,9 @@ import androidx.annotation.DrawableRes
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
@ -42,7 +44,9 @@ internal fun LazyListScope.swapTransactionsItems(
val item = swapTxs[it]
val (iconRes, tint) = when (item.activeStatus) {
ExchangeStatus.Verifying -> R.drawable.ic_alert_triangle_20 to TangemTheme.colors.icon.attention
ExchangeStatus.Failed -> R.drawable.ic_alert_circle_24 to TangemTheme.colors.icon.warning
ExchangeStatus.Failed, ExchangeStatus.Cancelled -> {
R.drawable.ic_alert_circle_24 to TangemTheme.colors.icon.warning
}
else -> null to null
}

View file

@ -125,6 +125,7 @@ internal class TokenDetailsViewModel @Inject constructor(
clickIntents = this,
appCurrencyProvider = Provider { selectedAppCurrencyFlow.value },
analyticsEventsHandlerProvider = Provider { analyticsEventsHandler },
currentStateProvider = Provider { uiState },
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrency,
)
@ -229,7 +230,7 @@ internal class TokenDetailsViewModel @Inject constructor(
delay = EXCHANGE_STATUS_UPDATE_DELAY,
task = {
runCatching(dispatchers.io) {
exchangeStatusFactory.updateSwapTxStatuses(swapTxs)
exchangeStatusFactory.updateSwapTxStatuses(uiState.swapTxs)
}
},
onSuccess = ::updateSwapTx,