Updated on 2026-08-14
This commit is contained in:
parent
199c2795ad
commit
d22cc0fe8b
9 changed files with 124 additions and 42 deletions
|
|
@ -198,9 +198,9 @@
|
|||
<string name="express_choose_providers_subtitle">Провайдеры проводят транзакции, обеспечивая плавный и эффективный обмен токенами</string>
|
||||
<string name="express_choose_providers_title">Выберите провайдера</string>
|
||||
<string name="express_exchange_notification_failed_text">Чтобы узнать причину, посетите сайт провайдера</string>
|
||||
<string name="express_exchange_notification_failed_title">Операция не выполнена провайдером</string>
|
||||
<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_notification_verification_title">Провайдер запрашивает прохождение верификации</string>
|
||||
<string name="express_exchange_token_list_subtitle">Список токенов в вашем кошельке</string>
|
||||
<string name="express_fetch_best_rates">Получение наилучших курсов...</string>
|
||||
<string name="express_provider">Провайдер</string>
|
||||
|
|
|
|||
|
|
@ -198,18 +198,22 @@
|
|||
<string name="express_choose_providers_title">Choose provider</string>
|
||||
<string name="express_estimated_amount">Estimated amount</string>
|
||||
<string name="express_exchange_by">Exchange by %s</string>
|
||||
<string name="express_exchange_notification_failed_text">Visit provider’s website to see why</string>
|
||||
<string name="express_exchange_notification_failed_text">Visit provider’s website to refund your money</string>
|
||||
<string name="express_exchange_notification_failed_title">Operation failed by provider</string>
|
||||
<string name="express_exchange_notification_verification_text">Visit provider’s website for verification</string>
|
||||
<string name="express_exchange_notification_verification_title">KYC verification required by provider</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>
|
||||
<string name="express_exchange_status_exchanged">Exchanged</string>
|
||||
<string name="express_exchange_status_exchanging">Exchanging</string>
|
||||
<string name="express_exchange_status_exchanging_active">Exchanging…</string>
|
||||
<string name="express_exchange_status_failed">Failed</string>
|
||||
<string name="express_exchange_status_received">Deposit received</string>
|
||||
<string name="express_exchange_status_receiving">Awaiting deposit</string>
|
||||
<string name="express_exchange_status_receiving_active">Awaiting deposit…</string>
|
||||
<string name="express_exchange_status_sending">Sending to you</string>
|
||||
<string name="express_exchange_status_sending_active">Sending to you…</string>
|
||||
<string name="express_exchange_status_sent">Sent</string>
|
||||
<string name="express_exchange_status_subtitle">Provider-sourced data. Estimated amount subject to change.</string>
|
||||
<string name="express_exchange_status_title">Exchange status</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.domain.tokens.models.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
class TokenExchangeAnalyticsEvent(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent("Token", event, params, null) {
|
||||
|
||||
class CexTx(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Notice - ChangeNow Swap",
|
||||
params = mapOf("Token" to token),
|
||||
)
|
||||
|
||||
class CexTxOpened(token: String, status: String) : TokenScreenAnalyticsEvent(
|
||||
event = "ChangeNow Swap Opened",
|
||||
params = mapOf("Token" to token, "Status" to status),
|
||||
)
|
||||
|
||||
class Verification(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Notice - KYC required",
|
||||
params = mapOf("Token" to token),
|
||||
)
|
||||
|
||||
class Fail(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Notice - Operation Fail",
|
||||
params = mapOf("Token" to token),
|
||||
)
|
||||
|
||||
class GoToProviderStatus(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Button - Go To Provider",
|
||||
params = mapOf("Token" to token, "Place" to "Status"),
|
||||
)
|
||||
|
||||
class GoToProviderKYC(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Button - Go To Provider",
|
||||
params = mapOf("Token" to token, "Place" to "KYC"),
|
||||
)
|
||||
|
||||
class GoToProviderFail(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Button - Go To Provider",
|
||||
params = mapOf("Token" to token, "Place" to "Fail"),
|
||||
)
|
||||
}
|
||||
|
|
@ -97,11 +97,11 @@ class DefaultSwapTransactionRepository(
|
|||
)
|
||||
}
|
||||
?.transactions
|
||||
?.dropWhile { it.txId == txId }
|
||||
?.filterNot { it.txId == txId }
|
||||
|
||||
val editedList =
|
||||
if (tokenTransactions.isNullOrEmpty()) {
|
||||
savedList?.dropWhile {
|
||||
savedList?.filterNot {
|
||||
it.checkId(
|
||||
checkUserWalletId = userWalletId,
|
||||
fromCurrencyId = fromCryptoCurrencyId,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
|||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.domain.txhistory.models.TxHistoryStateError
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadedTxHistoryConverter
|
||||
|
|
@ -243,14 +244,13 @@ internal class TokenDetailsStateFactory(
|
|||
return state.copy(notifications = notificationConverter.removeRentInfo(state))
|
||||
}
|
||||
|
||||
fun getStateWithExchangeStatusBottomSheet(txId: String): TokenDetailsState {
|
||||
val state = currentStateProvider()
|
||||
return state.copy(
|
||||
fun getStateWithExchangeStatusBottomSheet(swapTxState: SwapTransactionsState): TokenDetailsState {
|
||||
return currentStateProvider().copy(
|
||||
bottomSheetConfig = TangemBottomSheetConfig(
|
||||
isShow = true,
|
||||
onDismissRequest = clickIntents::onDismissBottomSheet,
|
||||
content = ExchangeStatusBottomSheetConfig(
|
||||
value = state.swapTxs.first { it.txId == txId },
|
||||
value = swapTxState,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
||||
|
||||
import com.tangem.common.Provider
|
||||
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.extensions.combinedReference
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.core.ui.utils.toDateFormat
|
||||
import com.tangem.core.ui.utils.toTimeFormat
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.models.analytics.TokenExchangeAnalyticsEvent
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.ExchangeStatusState
|
||||
|
|
@ -26,6 +28,8 @@ import java.math.BigDecimal
|
|||
|
||||
internal class TokenDetailsSwapTransactionsStateConverter(
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
private val cryptoCurrency: CryptoCurrency,
|
||||
private val analyticsEventsHandlerProvider: Provider<AnalyticsEventHandler>,
|
||||
appCurrencyProvider: Provider<AppCurrency>,
|
||||
) : Converter<Unit, PersistentList<SwapTransactionsState>> {
|
||||
|
||||
|
|
@ -89,8 +93,13 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
fromCryptoSymbol = fromCurrency.currency.symbol,
|
||||
fromFiatAmount = getFiatAmount(fromFiatAmount),
|
||||
fromCurrencyIcon = iconStateConverter.convert(fromCurrency),
|
||||
onClick = { clickIntents.onSwapTransactionClick(transaction.txId) },
|
||||
onGoToProviderClick = { clickIntents.onGoToProviderClick(transaction.status?.txUrl.orEmpty()) },
|
||||
onClick = { clickIntents.onSwapTransactionClick(transaction.txId, transaction.status?.status) },
|
||||
onGoToProviderClick = {
|
||||
analyticsEventsHandlerProvider().send(
|
||||
TokenExchangeAnalyticsEvent.GoToProviderStatus(cryptoCurrency.symbol),
|
||||
)
|
||||
clickIntents.onGoToProviderClick(url = transaction.status?.txUrl.orEmpty())
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -116,11 +125,27 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
private fun getNotification(status: ExchangeStatus?, txUrl: String?): ExchangeStatusNotifications? {
|
||||
if (txUrl == null) return null
|
||||
return when (status) {
|
||||
ExchangeStatus.Failed -> ExchangeStatusNotifications.Failed {
|
||||
clickIntents.onGoToProviderClick(txUrl)
|
||||
ExchangeStatus.Failed -> {
|
||||
analyticsEventsHandlerProvider().send(
|
||||
TokenExchangeAnalyticsEvent.Fail(cryptoCurrency.symbol),
|
||||
)
|
||||
ExchangeStatusNotifications.Failed {
|
||||
analyticsEventsHandlerProvider().send(
|
||||
TokenExchangeAnalyticsEvent.GoToProviderFail(cryptoCurrency.symbol),
|
||||
)
|
||||
clickIntents.onGoToProviderClick(txUrl)
|
||||
}
|
||||
}
|
||||
ExchangeStatus.Verifying -> ExchangeStatusNotifications.NeedVerification {
|
||||
clickIntents.onGoToProviderClick(txUrl)
|
||||
ExchangeStatus.Verifying -> {
|
||||
analyticsEventsHandlerProvider().send(
|
||||
TokenExchangeAnalyticsEvent.Verification(cryptoCurrency.symbol),
|
||||
)
|
||||
ExchangeStatusNotifications.NeedVerification {
|
||||
analyticsEventsHandlerProvider().send(
|
||||
TokenExchangeAnalyticsEvent.GoToProviderKYC(cryptoCurrency.symbol),
|
||||
)
|
||||
clickIntents.onGoToProviderClick(txUrl)
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
|
@ -150,10 +175,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
private fun waitStep(isNew: Boolean, isNewDone: Boolean) = ExchangeStatusState(
|
||||
status = ExchangeStatus.New,
|
||||
text = when {
|
||||
isNew -> combinedReference(
|
||||
TextReference.Res(R.string.express_exchange_status_receiving),
|
||||
TextReference.Str(STATUS_ACTIVE_DOTS),
|
||||
)
|
||||
isNew -> TextReference.Res(R.string.express_exchange_status_receiving_active)
|
||||
else -> TextReference.Res(R.string.express_exchange_status_receiving)
|
||||
},
|
||||
isActive = isNew,
|
||||
|
|
@ -163,10 +185,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
private fun confirmStep(isConfirming: Boolean, isConfirmingDone: Boolean) = ExchangeStatusState(
|
||||
status = ExchangeStatus.Confirming,
|
||||
text = when {
|
||||
isConfirming -> combinedReference(
|
||||
TextReference.Res(R.string.express_exchange_status_confirming),
|
||||
TextReference.Str(STATUS_ACTIVE_DOTS),
|
||||
)
|
||||
isConfirming -> TextReference.Res(R.string.express_exchange_status_confirming_active)
|
||||
isConfirmingDone -> TextReference.Res(R.string.express_exchange_status_confirmed)
|
||||
else -> TextReference.Res(R.string.express_exchange_status_confirming)
|
||||
},
|
||||
|
|
@ -195,10 +214,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
else -> ExchangeStatusState(
|
||||
status = ExchangeStatus.Exchanging,
|
||||
text = when {
|
||||
isExchanging -> combinedReference(
|
||||
TextReference.Res(R.string.express_exchange_status_exchanging),
|
||||
TextReference.Str(STATUS_ACTIVE_DOTS),
|
||||
)
|
||||
isExchanging -> TextReference.Res(R.string.express_exchange_status_exchanging_active)
|
||||
isExchangingDone -> TextReference.Res(R.string.express_exchange_status_exchanged)
|
||||
else -> TextReference.Res(R.string.express_exchange_status_exchanging)
|
||||
},
|
||||
|
|
@ -210,18 +226,11 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
private fun sendStep(isSending: Boolean, isSendingDone: Boolean) = ExchangeStatusState(
|
||||
status = ExchangeStatus.Sending,
|
||||
text = when {
|
||||
isSending -> combinedReference(
|
||||
TextReference.Res(R.string.express_exchange_status_sending),
|
||||
TextReference.Str(STATUS_ACTIVE_DOTS),
|
||||
)
|
||||
isSending -> TextReference.Res(R.string.express_exchange_status_sending_active)
|
||||
isSendingDone -> TextReference.Res(R.string.express_exchange_status_sent)
|
||||
else -> TextReference.Res(R.string.express_exchange_status_sending)
|
||||
},
|
||||
isActive = isSending,
|
||||
isDone = isSendingDone,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
private const val STATUS_ACTIVE_DOTS = "…"
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels
|
|||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -35,19 +36,22 @@ internal class ExchangeStatusFactory(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val analyticsEventsHandlerProvider: Provider<AnalyticsEventHandler>,
|
||||
private val userWalletId: UserWalletId,
|
||||
private val cryptoCurrencyId: CryptoCurrency.ID,
|
||||
private val cryptoCurrency: CryptoCurrency,
|
||||
) {
|
||||
|
||||
private val swapTransactionsStateConverter by lazy {
|
||||
TokenDetailsSwapTransactionsStateConverter(
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
analyticsEventsHandlerProvider = analyticsEventsHandlerProvider,
|
||||
)
|
||||
}
|
||||
|
||||
operator fun invoke() = combine(
|
||||
flow = swapTransactionRepository.getTransactions(userWalletId, cryptoCurrencyId),
|
||||
flow = swapTransactionRepository.getTransactions(userWalletId, cryptoCurrency.id),
|
||||
flow2 = getWalletCryptoCurrencies().conflate(),
|
||||
) { savedTransactions, cryptoCurrenciesStatusList ->
|
||||
innerLoadSwapState(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels
|
|||
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
|
||||
interface TokenDetailsClickIntents {
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ interface TokenDetailsClickIntents {
|
|||
|
||||
fun onCloseRentInfoNotification()
|
||||
|
||||
fun onSwapTransactionClick(txId: String)
|
||||
fun onSwapTransactionClick(txId: String, status: ExchangeStatus?)
|
||||
|
||||
fun onGoToProviderClick(url: String)
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import com.tangem.domain.tokens.*
|
|||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.models.analytics.TokenExchangeAnalyticsEvent
|
||||
import com.tangem.domain.tokens.models.analytics.TokenReceiveAnalyticsEvent
|
||||
import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent
|
||||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||
|
|
@ -35,9 +36,11 @@ import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
|||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.swap.domain.SwapRepository
|
||||
import com.tangem.feature.swap.domain.SwapTransactionRepository
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
|
|
@ -117,8 +120,9 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
dispatchers = dispatchers,
|
||||
clickIntents = this,
|
||||
appCurrencyProvider = Provider { selectedAppCurrencyFlow.value },
|
||||
analyticsEventsHandlerProvider = Provider { analyticsEventsHandler },
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -212,6 +216,9 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
swapTxStatusTaskScheduler.cancelTask()
|
||||
exchangeStatusFactory.invoke()
|
||||
.onEach { swapTxs ->
|
||||
if (swapTxs.isNotEmpty()) {
|
||||
analyticsEventsHandler.send(TokenExchangeAnalyticsEvent.CexTx(cryptoCurrency.symbol))
|
||||
}
|
||||
swapTxStatusTaskScheduler.scheduleTask(
|
||||
viewModelScope,
|
||||
PeriodicTask(
|
||||
|
|
@ -533,8 +540,21 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
uiState = stateFactory.getStateWithRemovedRentNotification()
|
||||
}
|
||||
|
||||
override fun onSwapTransactionClick(txId: String) {
|
||||
uiState = stateFactory.getStateWithExchangeStatusBottomSheet(txId)
|
||||
override fun onSwapTransactionClick(txId: String, status: ExchangeStatus?) {
|
||||
val swapTxState = uiState.swapTxs.first { it.txId == txId }
|
||||
analyticsEventsHandler.send(
|
||||
TokenExchangeAnalyticsEvent.CexTxOpened(cryptoCurrency.symbol, status?.name.orEmpty()),
|
||||
)
|
||||
when (swapTxState.notification.value) {
|
||||
is ExchangeStatusNotifications.NeedVerification -> {
|
||||
analyticsEventsHandler.send(TokenExchangeAnalyticsEvent.Verification(cryptoCurrency.symbol))
|
||||
}
|
||||
is ExchangeStatusNotifications.Failed -> {
|
||||
analyticsEventsHandler.send(TokenExchangeAnalyticsEvent.Fail(cryptoCurrency.symbol))
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
uiState = stateFactory.getStateWithExchangeStatusBottomSheet(swapTxState)
|
||||
}
|
||||
|
||||
override fun onGoToProviderClick(url: String) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue