From d22cc0fe8b61ea19122902c728774029197aec85 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 1 Dec 2023 17:24:50 +0300 Subject: [PATCH] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 4 +- core/res/src/main/res/values/strings.xml | 6 +- .../analytics/TokenExchangeAnalyticsEvent.kt | 44 +++++++++++++ .../swap/DefaultSwapTransactionRepository.kt | 4 +- .../state/factory/TokenDetailsStateFactory.kt | 8 +-- ...enDetailsSwapTransactionsStateConverter.kt | 63 +++++++++++-------- .../viewmodels/ExchangeStatusFactory.kt | 8 ++- .../viewmodels/TokenDetailsClickIntents.kt | 3 +- .../viewmodels/TokenDetailsViewModel.kt | 26 +++++++- 9 files changed, 124 insertions(+), 42 deletions(-) create mode 100644 domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 8805ebe16d..13ae7d8b04 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -198,9 +198,9 @@ Провайдеры проводят транзакции, обеспечивая плавный и эффективный обмен токенами Выберите провайдера Чтобы узнать причину, посетите сайт провайдера - Операция не выполнена провайдером + Чтобы вернуть ваши деньги, посетите сайт провайдера Посетите сайт провайдера для проверки - Провайдер: требуется верификация + Провайдер запрашивает прохождение верификации Список токенов в вашем кошельке Получение наилучших курсов... Провайдер diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 62304b3e12..44af8e6ed5 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -198,18 +198,22 @@ Choose provider Estimated amount Exchange by %s - Visit provider’s website to see why + Visit provider’s website to refund your money Operation failed by provider Visit provider’s website for verification KYC verification required by provider Confirmed Confirming + Confirming… Exchanged Exchanging + Exchanging… Failed Deposit received Awaiting deposit + Awaiting deposit… Sending to you + Sending to you… Sent Provider-sourced data. Estimated amount subject to change. Exchange status diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt new file mode 100644 index 0000000000..a795ccd2af --- /dev/null +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt @@ -0,0 +1,44 @@ +package com.tangem.domain.tokens.models.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent + +class TokenExchangeAnalyticsEvent( + event: String, + params: Map = 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"), + ) +} \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt index 63c8aecb72..d60d26a19e 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt @@ -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, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 368164ce46..44e7b1f2c4 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -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, ), ), ) 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 c0be53eb94..39d60d2436 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 @@ -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, appCurrencyProvider: Provider, ) : Converter> { @@ -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 = "…" - } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt index d92306d03c..b1441a921a 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/ExchangeStatusFactory.kt @@ -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, + private val analyticsEventsHandlerProvider: Provider, 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( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt index 95093f7fdb..64a0d6dd2e 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt @@ -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) } \ No newline at end of file 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 126298c471..a730e6edd2 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 @@ -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) {