From b750588ef9160adcce62ef3dfab3bd7ee17b5ea3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 5 Dec 2024 12:45:38 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/OnrampDomainModule.kt | 10 ++- .../DefaultSwapTransactionStatusStore.kt | 6 +- .../swaptx/SwapTransactionStatusStore.kt | 6 +- .../data/onramp/DefaultOnrampRepository.kt | 21 +++---- .../onramp/converters/TransactionConverter.kt | 4 ++ .../onramp/models/OnrampTransactionDTO.kt | 4 ++ .../onramp/model/cache/OnrampTransaction.kt | 4 ++ .../domain/onramp/GetOnrampStatusUseCase.kt | 6 +- .../OnrampUpdateTransactionStatusUseCase.kt | 14 +++++ .../onramp/analytics/OnrampAnalyticsEvent.kt | 62 ++++++++++++++++--- .../analytics/TokenOnrampAnalyticsEvent.kt | 46 ++++++++++++++ .../main/model/OnrampMainComponentModel.kt | 19 +++++- .../providers/model/SelectProviderModel.kt | 4 +- .../model/OnrampSuccessComponentModel.kt | 16 ++++- .../express/ExpressTransactionStateUM.kt | 1 + ...nDetailsOnrampTransactionStateConverter.kt | 18 +++++- ...enDetailsSwapTransactionsStateConverter.kt | 8 +-- .../factory/express/ExchangeStatusFactory.kt | 29 +++++---- .../factory/express/ExpressStatusFactory.kt | 17 +++-- .../factory/express/OnrampStatusFactory.kt | 48 ++++++++++++++ .../viewmodels/TokenDetailsViewModel.kt | 1 - 21 files changed, 277 insertions(+), 67 deletions(-) create mode 100644 domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampUpdateTransactionStatusUseCase.kt create mode 100644 domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenOnrampAnalyticsEvent.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt index 3204990b62..3b83197304 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt @@ -56,12 +56,10 @@ internal object OnrampDomainModule { @Singleton fun provideGetOnrampStatusUseCase( onrampRepository: OnrampRepository, - onrampTransactionRepository: OnrampTransactionRepository, onrampErrorResolver: OnrampErrorResolver, ): GetOnrampStatusUseCase { return GetOnrampStatusUseCase( onrampRepository, - onrampTransactionRepository, onrampErrorResolver, ) } @@ -104,6 +102,14 @@ internal object OnrampDomainModule { return OnrampSaveTransactionUseCase(onrampTransactionRepository) } + @Provides + @Singleton + fun provideOnrampUpdateTransactionStatusUseCase( + onrampTransactionRepository: OnrampTransactionRepository, + ): OnrampUpdateTransactionStatusUseCase { + return OnrampUpdateTransactionStatusUseCase(onrampTransactionRepository) + } + @Provides @Singleton fun provideGetOnrampPaymentMethodsUseCase(onrampRepository: OnrampRepository): GetOnrampPaymentMethodsUseCase { diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/DefaultSwapTransactionStatusStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/DefaultSwapTransactionStatusStore.kt index 50b4f08c9d..bb094d028f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/DefaultSwapTransactionStatusStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/DefaultSwapTransactionStatusStore.kt @@ -3,10 +3,10 @@ package com.tangem.datasource.local.swaptx import com.tangem.datasource.local.datastore.core.StringKeyDataStore internal class DefaultSwapTransactionStatusStore( - private val dataStore: StringKeyDataStore, -) : SwapTransactionStatusStore, StringKeyDataStore by dataStore { + private val dataStore: StringKeyDataStore, +) : SwapTransactionStatusStore, StringKeyDataStore by dataStore { override suspend fun getTransactionStatus(txId: String) = getSyncOrNull(txId) - override suspend fun setTransactionStatus(txId: String, status: ExchangeAnalyticsStatus) = store(txId, status) + override suspend fun setTransactionStatus(txId: String, status: ExpressAnalyticsStatus) = store(txId, status) } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt index cf99b7c4ac..033f71b6ac 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt @@ -4,12 +4,12 @@ package com.tangem.datasource.local.swaptx * Runtime cache for storing swap transactions statuses sent to analytics */ interface SwapTransactionStatusStore { - suspend fun getTransactionStatus(txId: String): ExchangeAnalyticsStatus? + suspend fun getTransactionStatus(txId: String): ExpressAnalyticsStatus? - suspend fun setTransactionStatus(txId: String, status: ExchangeAnalyticsStatus) + suspend fun setTransactionStatus(txId: String, status: ExpressAnalyticsStatus) } -enum class ExchangeAnalyticsStatus(val value: String) { +enum class ExpressAnalyticsStatus(val value: String) { WaitingTxHash("Waiting tx hash"), InProgress("In Progress"), Done("Done"), diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt index f4f5c141f9..77c83a2937 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt @@ -51,7 +51,6 @@ import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.withContext import org.joda.time.DateTime import timber.log.Timber -import java.math.BigDecimal import java.util.UUID @Suppress("LongParameterList", "LargeClass", "TooManyFunctions") @@ -303,13 +302,12 @@ internal class DefaultOnrampRepository( createOnrampTransaction( txId = data.txId, + quote = quote, onrampDataJson = dataJson, userWalletId = userWalletId, currency = currency, - provider = quote.provider, cryptoCurrency = cryptoCurrency, - fromAmount = quote.fromAmount.value, - toAmount = quote.toAmount.value, + residency = country.name, ) } else { throw OnrampRedirectError.VerificationFailed @@ -367,29 +365,30 @@ internal class DefaultOnrampRepository( private fun createOnrampTransaction( txId: String, + quote: OnrampProviderWithQuote.Data, onrampDataJson: OnrampDataJson, userWalletId: UserWalletId, currency: OnrampCurrency, - provider: OnrampProvider, cryptoCurrency: CryptoCurrency, - fromAmount: BigDecimal, - toAmount: BigDecimal, + residency: String, ): OnrampTransaction { return OnrampTransaction( txId = txId, userWalletId = userWalletId, - fromAmount = fromAmount, + fromAmount = quote.fromAmount.value, fromCurrency = currency, - toAmount = toAmount, + toAmount = quote.toAmount.value, toCurrencyId = cryptoCurrency.id.value, status = OnrampStatus.Status.Expired, externalTxUrl = onrampDataJson.externalTxUrl, externalTxId = onrampDataJson.externalTxId, timestamp = DateTime.now().millis, - providerName = provider.info.name, - providerImageUrl = provider.info.imageLarge, + providerName = quote.provider.info.name, + providerImageUrl = quote.provider.info.imageLarge, providerType = ExchangeProviderType.ONRAMP.name, redirectUrl = onrampDataJson.widgetUrl, + paymentMethod = quote.paymentMethod.name, + residency = residency, ) } diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/converters/TransactionConverter.kt b/data/onramp/src/main/java/com/tangem/data/onramp/converters/TransactionConverter.kt index 9b29c49779..1ca843a50e 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/converters/TransactionConverter.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/converters/TransactionConverter.kt @@ -26,6 +26,8 @@ internal class TransactionConverter : TwoWayConverter { return Either.catch { - val status = onrampRepository.getStatus(txId) - onrampTransactionRepository.updateTransactionStatus(txId = txId, status = status.status) - status + onrampRepository.getStatus(txId) }.mapLeft { errorResolver.resolve(it) } diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampUpdateTransactionStatusUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampUpdateTransactionStatusUseCase.kt new file mode 100644 index 0000000000..1a9349e91d --- /dev/null +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampUpdateTransactionStatusUseCase.kt @@ -0,0 +1,14 @@ +package com.tangem.domain.onramp + +import arrow.core.Either +import com.tangem.domain.onramp.model.OnrampStatus +import com.tangem.domain.onramp.repositories.OnrampTransactionRepository + +class OnrampUpdateTransactionStatusUseCase( + private val onrampTransactionRepository: OnrampTransactionRepository, +) { + + suspend operator fun invoke(txId: String, status: OnrampStatus.Status) = Either.catch { + onrampTransactionRepository.updateTransactionStatus(txId = txId, status = status) + } +} \ No newline at end of file diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/analytics/OnrampAnalyticsEvent.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/analytics/OnrampAnalyticsEvent.kt index fe3ac20287..ce1236faf1 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/analytics/OnrampAnalyticsEvent.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/analytics/OnrampAnalyticsEvent.kt @@ -14,12 +14,12 @@ sealed class OnrampAnalyticsEvent( data class ScreenOpened( val source: OnrampSource, - val cryptoCurrency: String, + val tokenSymbol: String, ) : OnrampAnalyticsEvent( event = "Buy Screen Opened", params = mapOf( "Source" to source.analyticsName, - "Token" to cryptoCurrency, + "Token" to tokenSymbol, ), ) @@ -65,40 +65,82 @@ sealed class OnrampAnalyticsEvent( data class ProviderCalculated( private val providerName: String, - private val cryptoCurrency: String, + private val tokenSymbol: String, + private val paymentMethod: String, ) : OnrampAnalyticsEvent( event = "Provider Calculated", params = mapOf( "Provider" to providerName, - "Token" to cryptoCurrency, + "Token" to tokenSymbol, + "Payment Method" to paymentMethod, ), ) data object PaymentMethodsScreenOpened : OnrampAnalyticsEvent(event = "Payment Method Screen Opened") - data object OnPaymentMethodChosen : OnrampAnalyticsEvent(event = "Method Chosen") + data class OnPaymentMethodChosen( + private val paymentMethod: String, + ) : OnrampAnalyticsEvent( + event = "Method Chosen", + params = mapOf( + "Payment Method" to paymentMethod, + ), + ) data class OnProviderChosen( private val providerName: String, - private val cryptoCurrency: String, + private val tokenSymbol: String, ) : OnrampAnalyticsEvent( event = "Provider Chosen", params = mapOf( "Provider" to providerName, - "Token" to cryptoCurrency, + "Token" to tokenSymbol, ), ) data class OnBuyClick( private val providerName: String, private val currency: String, - private val cryptoCurrency: String, + private val tokenSymbol: String, ) : OnrampAnalyticsEvent( - event = "Provider Chosen", + event = "Button - Buy", params = mapOf( "Provider" to providerName, "Currency" to currency, - "Token" to cryptoCurrency, + "Token" to tokenSymbol, + ), + ) + + data class SuccessScreenOpened( + private val providerName: String, + private val currency: String, + private val tokenSymbol: String, + private val residence: String, + private val paymentMethod: String, + ) : OnrampAnalyticsEvent( + event = "Buying In Progress Screen Opened", + params = mapOf( + "Token" to tokenSymbol, + "Provider" to providerName, + "Residence" to residence, + "Currency" to currency, + "Method" to paymentMethod, + ), + ) + + data object MinAmountError : OnrampAnalyticsEvent(event = "Error - Min Amount") + data object MaxAmountError : OnrampAnalyticsEvent(event = "Error - Max Amount") + + data class Errors( + private val tokenSymbol: String, + private val providerName: String, + private val errorCode: String, + ) : OnrampAnalyticsEvent( + event = "Errors", + params = mapOf( + "Token" to tokenSymbol, + "Provider" to providerName, + "Error Code" to errorCode, ), ) } \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenOnrampAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenOnrampAnalyticsEvent.kt new file mode 100644 index 0000000000..50eaec64e5 --- /dev/null +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenOnrampAnalyticsEvent.kt @@ -0,0 +1,46 @@ +package com.tangem.domain.tokens.model.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam.Key.CURRENCY +import com.tangem.core.analytics.models.AnalyticsParam.Key.PROVIDER +import com.tangem.core.analytics.models.AnalyticsParam.Key.STATUS +import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM + +class TokenOnrampAnalyticsEvent( + event: String, + params: Map = mapOf(), +) : AnalyticsEvent("Onramp", event, params, null) { + + class OnrampStatusOpened(tokenSymbol: String, provider: String, fiatCurrency: String) : TokenScreenAnalyticsEvent( + event = "Onramp Status Opened", + params = mapOf( + TOKEN_PARAM to tokenSymbol, + PROVIDER to provider, + CURRENCY to fiatCurrency, + ), + ) + + class OnrampStatusChanged(tokenSymbol: String, status: String, provider: String, fiatCurrency: String) : + TokenScreenAnalyticsEvent( + event = "Onramp Status", + params = mapOf( + TOKEN_PARAM to tokenSymbol, + STATUS to status, + PROVIDER to provider, + CURRENCY to fiatCurrency, + ), + ) + + data object GoToProvider : TokenScreenAnalyticsEvent(event = "Button - Go To Provider") + + data class NoticeKYC( + val tokenSymbol: String, + val provider: String, + ) : TokenScreenAnalyticsEvent( + event = "Notice - KYC", + params = mapOf( + TOKEN_PARAM to tokenSymbol, + PROVIDER to provider, + ), + ) +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt index cc5155dbc8..8c63935d30 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt @@ -1,5 +1,6 @@ package com.tangem.features.onramp.main.model +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.res.stringResource import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate @@ -19,6 +20,7 @@ import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent import com.tangem.domain.onramp.model.OnrampAvailability import com.tangem.domain.onramp.model.OnrampCurrency import com.tangem.domain.onramp.model.OnrampProviderWithQuote +import com.tangem.domain.onramp.model.OnrampQuote import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.main.OnrampMainComponent @@ -34,6 +36,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber +import java.math.BigDecimal import javax.inject.Inject @Suppress("LongParameterList") @@ -73,6 +76,8 @@ internal class OnrampMainComponentModel @Inject constructor( val state: StateFlow get() = _state.asStateFlow() val bottomSheetNavigation: SlotNavigation = SlotNavigation() + private val lastAmount = mutableStateOf(BigDecimal.ZERO) + init { sendScreenOpenAnalytics() checkResidenceCountry() @@ -83,7 +88,7 @@ internal class OnrampMainComponentModel @Inject constructor( analyticsEventHandler.send( OnrampAnalyticsEvent.ScreenOpened( source = params.source, - cryptoCurrency = params.cryptoCurrency.name, + tokenSymbol = params.cryptoCurrency.symbol, ), ) } @@ -143,6 +148,16 @@ internal class OnrampMainComponentModel @Inject constructor( getOnrampQuotesUseCase.invoke() .onEach { maybeQuotes -> val quote = maybeQuotes.getOrNull()?.firstOrNull() ?: return@onEach + if (quote is OnrampQuote.Data && lastAmount.value != quote.fromAmount.value) { + lastAmount.value = quote.fromAmount.value + analyticsEventHandler.send( + OnrampAnalyticsEvent.ProviderCalculated( + providerName = quote.provider.info.name, + tokenSymbol = params.cryptoCurrency.symbol, + paymentMethod = quote.paymentMethod.name, + ), + ) + } _state.update { amountStateFactory.getAmountSecondaryUpdatedState(quote) } } .launchIn(modelScope) @@ -166,7 +181,7 @@ internal class OnrampMainComponentModel @Inject constructor( OnrampAnalyticsEvent.OnBuyClick( providerName = quote.provider.info.name, currency = currentContentState.amountBlockState.currencyUM.code, - cryptoCurrency = params.cryptoCurrency.name, + tokenSymbol = params.cryptoCurrency.symbol, ), ) params.openRedirectPage(quote) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt index 753d8ab225..21a7ba9774 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt @@ -108,7 +108,7 @@ internal class SelectProviderModel @Inject constructor( } private fun onPaymentMethodSelected(paymentMethod: OnrampPaymentMethod) { - analyticsEventHandler.send(OnrampAnalyticsEvent.OnPaymentMethodChosen) + analyticsEventHandler.send(OnrampAnalyticsEvent.OnPaymentMethodChosen(paymentMethod = paymentMethod.name)) modelScope.launch { saveSelectedPaymentMethod.invoke(paymentMethod) _state.update { state -> @@ -141,7 +141,7 @@ internal class SelectProviderModel @Inject constructor( analyticsEventHandler.send( OnrampAnalyticsEvent.OnProviderChosen( providerName = quote.provider.info.name, - cryptoCurrency = params.cryptoCurrency.name, + tokenSymbol = params.cryptoCurrency.symbol, ), ) params.onProviderClick(quote) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/model/OnrampSuccessComponentModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/model/OnrampSuccessComponentModel.kt index 6b0e80f76d..e277fdbce7 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/model/OnrampSuccessComponentModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/model/OnrampSuccessComponentModel.kt @@ -1,12 +1,14 @@ package com.tangem.features.onramp.success.model import arrow.core.getOrElse +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.navigation.url.UrlOpener import com.tangem.domain.onramp.GetOnrampStatusUseCase import com.tangem.domain.onramp.GetOnrampTransactionUseCase import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase +import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent import com.tangem.domain.onramp.model.OnrampStatus import com.tangem.domain.onramp.model.cache.OnrampTransaction import com.tangem.domain.tokens.GetCryptoCurrencyUseCase @@ -31,6 +33,7 @@ internal class OnrampSuccessComponentModel @Inject constructor( private val getOnrampStatusUseCase: GetOnrampStatusUseCase, private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase, private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase, + private val analyticsEventHandler: AnalyticsEventHandler, paramsContainer: ParamsContainer, ) : Model(), OnrampSuccessClickIntents { @@ -64,7 +67,7 @@ internal class OnrampSuccessComponentModel @Inject constructor( } private suspend fun loadTransactionStatus(transaction: OnrampTransaction) { - val cryptoCurrencies = getCryptoCurrencyUseCase( + val cryptoCurrency = getCryptoCurrencyUseCase( transaction.userWalletId, transaction.toCurrencyId, ).getOrElse { error("Crypto currency not found") } @@ -75,9 +78,18 @@ internal class OnrampSuccessComponentModel @Inject constructor( Timber.e(it.toString()) }, ifRight = { status -> + analyticsEventHandler.send( + OnrampAnalyticsEvent.SuccessScreenOpened( + providerName = transaction.providerName, + currency = transaction.fromCurrency.code, + tokenSymbol = cryptoCurrency.symbol, + residence = transaction.residency, + paymentMethod = transaction.paymentMethod, + ), + ) _state.update { SetOnrampSuccessContentConverter( - cryptoCurrency = cryptoCurrencies, + cryptoCurrency = cryptoCurrency, transaction = transaction, goToProviderClick = ::goToProviderClick, ).convert(status) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt index fb777dc79d..bb94a805d3 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/express/ExpressTransactionStateUM.kt @@ -33,6 +33,7 @@ internal sealed class ExpressTransactionStateUM { val providerImageUrl: String, // todo onramp fix after SwapProvider moved to own module val providerType: String, // todo onramp fix after SwapProvider moved to own module val activeStatus: OnrampStatus.Status, + val fromCurrencyCode: String, ) : ExpressTransactionStateUM() } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt index b0a556cbc9..dd8b9bc4e9 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt @@ -6,6 +6,7 @@ import com.tangem.common.ui.expressStatus.state.ExpressStatusItemUM import com.tangem.common.ui.expressStatus.state.ExpressStatusUM import com.tangem.common.ui.notifications.ExpressNotificationsUM import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.extensions.resourceReference @@ -21,6 +22,7 @@ import com.tangem.domain.onramp.model.OnrampStatus import com.tangem.domain.onramp.model.cache.OnrampTransaction import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateIconUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateInfoUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM @@ -35,6 +37,7 @@ internal class TokenDetailsOnrampTransactionStateConverter( private val cryptoCurrency: CryptoCurrency, private val cryptoCurrencyStatusProvider: Provider, private val appCurrencyProvider: Provider, + private val analyticsEventHandler: AnalyticsEventHandler, ) : Converter { private val iconStateConverter = CryptoCurrencyToIconStateConverter() @@ -46,7 +49,7 @@ internal class TokenDetailsOnrampTransactionStateConverter( info = ExpressTransactionStateInfoUM( title = resourceReference(id = R.string.express_status_buying, wrappedList(cryptoCurrency.name)), status = convertStatuses(value.status, value.externalTxUrl), - notification = getNotification(value.status, value.externalTxUrl), + notification = getNotification(value.status, value.externalTxUrl, value.providerName), txId = value.txId, txExternalId = value.externalTxId, txExternalUrl = value.externalTxUrl, @@ -86,20 +89,29 @@ internal class TokenDetailsOnrampTransactionStateConverter( fallbackResId = R.drawable.ic_currency_24, ), iconState = getIconState(value.status), - onGoToProviderClick = clickIntents::onGoToProviderClick, + onGoToProviderClick = { + analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider) + clickIntents.onGoToProviderClick(it) + }, onClick = { clickIntents.onExpressTransactionClick(value.txId) }, ), providerName = value.providerName, providerImageUrl = value.providerImageUrl, providerType = value.providerType, activeStatus = value.status, + fromCurrencyCode = value.fromCurrency.code, ) } - private fun getNotification(status: OnrampStatus.Status, externalTxUrl: String?): NotificationUM? { + private fun getNotification( + status: OnrampStatus.Status, + externalTxUrl: String?, + providerName: String, + ): NotificationUM? { if (externalTxUrl == null) return null return when (status) { OnrampStatus.Status.Verifying -> { + analyticsEventHandler.send(TokenOnrampAnalyticsEvent.NoticeKYC(cryptoCurrency.symbol, providerName)) ExpressNotificationsUM.NeedVerification { clickIntents.onGoToProviderClick(externalTxUrl) } 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 4ae19831d2..d4738e748b 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 @@ -43,7 +43,7 @@ import java.util.Locale internal class TokenDetailsSwapTransactionsStateConverter( private val clickIntents: TokenDetailsClickIntents, private val cryptoCurrency: CryptoCurrency, - private val analyticsEventsHandlerProvider: Provider, + private val analyticsEventsHandler: AnalyticsEventHandler, appCurrencyProvider: Provider, ) : Converter> { @@ -157,7 +157,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( fromAmountSymbol = fromCryptoCurrency.symbol, onClick = { clickIntents.onExpressTransactionClick(transaction.txId) }, onGoToProviderClick = { url -> - analyticsEventsHandlerProvider().send( + analyticsEventsHandler.send( TokenExchangeAnalyticsEvent.GoToProviderStatus(cryptoCurrency.symbol), ) clickIntents.onGoToProviderClick(url = url) @@ -190,7 +190,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( ExchangeStatus.Failed -> { if (txUrl == null) return null ExchangeStatusNotifications.Failed { - analyticsEventsHandlerProvider().send( + analyticsEventsHandler.send( TokenExchangeAnalyticsEvent.GoToProviderFail(cryptoCurrency.symbol), ) clickIntents.onGoToProviderClick(txUrl) @@ -199,7 +199,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( ExchangeStatus.Verifying -> { if (txUrl == null) return null ExchangeStatusNotifications.NeedVerification { - analyticsEventsHandlerProvider().send( + analyticsEventsHandler.send( TokenExchangeAnalyticsEvent.GoToProviderKYC(cryptoCurrency.symbol), ) clickIntents.onGoToProviderClick(txUrl) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt index 034bb903d3..545ff398df 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt @@ -1,7 +1,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.datasource.local.swaptx.ExchangeAnalyticsStatus +import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase @@ -38,9 +38,9 @@ internal class ExchangeStatusFactory @AssistedInject constructor( private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, private val swapTransactionStatusStore: SwapTransactionStatusStore, + private val analyticsEventsHandler: AnalyticsEventHandler, @Assisted private val clickIntents: TokenDetailsClickIntents, @Assisted private val appCurrencyProvider: Provider, - @Assisted private val analyticsEventsHandlerProvider: Provider, @Assisted private val currentStateProvider: Provider, @Assisted private val userWalletId: UserWalletId, @Assisted private val cryptoCurrency: CryptoCurrency, @@ -51,7 +51,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( clickIntents = clickIntents, cryptoCurrency = cryptoCurrency, appCurrencyProvider = appCurrencyProvider, - analyticsEventsHandlerProvider = analyticsEventsHandlerProvider, + analyticsEventsHandler = analyticsEventsHandler, ) } @@ -132,7 +132,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( val savedStatus = swapTransactionStatusStore.getTransactionStatus(txId) if (savedStatus != status) { - analyticsEventsHandlerProvider().send( + analyticsEventsHandler.send( TokenExchangeAnalyticsEvent.CexTxStatusChanged(cryptoCurrency.symbol, status.value, provider.name), ) swapTransactionStatusStore.setTransactionStatus(txId, status) @@ -183,22 +183,22 @@ internal class ExchangeStatusFactory @AssistedInject constructor( this == ExchangeStatus.Unknown } - private fun toAnalyticStatus(status: ExchangeStatus?): ExchangeAnalyticsStatus? { + private fun toAnalyticStatus(status: ExchangeStatus?): ExpressAnalyticsStatus? { return when (status) { ExchangeStatus.New, ExchangeStatus.Waiting, ExchangeStatus.Sending, ExchangeStatus.Confirming, ExchangeStatus.Exchanging, - -> ExchangeAnalyticsStatus.InProgress - ExchangeStatus.WaitingTxHash -> ExchangeAnalyticsStatus.WaitingTxHash - ExchangeStatus.Verifying -> ExchangeAnalyticsStatus.KYC - ExchangeStatus.Failed -> ExchangeAnalyticsStatus.Fail - ExchangeStatus.TxFailed -> ExchangeAnalyticsStatus.FailTx - ExchangeStatus.Finished -> ExchangeAnalyticsStatus.Done - ExchangeStatus.Refunded -> ExchangeAnalyticsStatus.Refunded - ExchangeStatus.Cancelled -> ExchangeAnalyticsStatus.Cancelled - ExchangeStatus.Unknown -> ExchangeAnalyticsStatus.Unknown + -> ExpressAnalyticsStatus.InProgress + ExchangeStatus.WaitingTxHash -> ExpressAnalyticsStatus.WaitingTxHash + ExchangeStatus.Verifying -> ExpressAnalyticsStatus.KYC + ExchangeStatus.Failed -> ExpressAnalyticsStatus.Fail + ExchangeStatus.TxFailed -> ExpressAnalyticsStatus.FailTx + ExchangeStatus.Finished -> ExpressAnalyticsStatus.Done + ExchangeStatus.Refunded -> ExpressAnalyticsStatus.Refunded + ExchangeStatus.Cancelled -> ExpressAnalyticsStatus.Cancelled + ExchangeStatus.Unknown -> ExpressAnalyticsStatus.Unknown else -> null } } @@ -216,7 +216,6 @@ internal class ExchangeStatusFactory @AssistedInject constructor( fun create( clickIntents: TokenDetailsClickIntents, appCurrencyProvider: Provider, - analyticsEventsHandlerProvider: Provider, currentStateProvider: Provider, userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt index 7d14266a26..92a6a2508c 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt @@ -6,6 +6,7 @@ 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.model.analytics.TokenExchangeAnalyticsEvent +import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.swap.domain.models.domain.ExchangeStatus import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState @@ -32,7 +33,6 @@ internal class ExpressStatusFactory @AssistedInject constructor( @Assisted private val clickIntents: TokenDetailsClickIntents, @Assisted private val cryptoCurrency: CryptoCurrency, @Assisted appCurrencyProvider: Provider, - @Assisted analyticsEventsHandlerProvider: Provider, @Assisted userWalletId: UserWalletId, @Assisted cryptoCurrencyStatusProvider: Provider, private val dispatchers: CoroutineDispatcherProvider, @@ -45,7 +45,6 @@ internal class ExpressStatusFactory @AssistedInject constructor( exchangeStatusFactory.create( clickIntents = clickIntents, appCurrencyProvider = appCurrencyProvider, - analyticsEventsHandlerProvider = analyticsEventsHandlerProvider, currentStateProvider = currentStateProvider, userWalletId = userWalletId, cryptoCurrency = cryptoCurrency, @@ -108,7 +107,18 @@ internal class ExpressStatusFactory @AssistedInject constructor( } fun getStateWithExpressStatusBottomSheet(expressState: ExpressTransactionStateUM): TokenDetailsState { - analyticsEventsHandler.send(TokenExchangeAnalyticsEvent.CexTxStatusOpened(cryptoCurrency.symbol)) + val analyticEvent = when (expressState) { + is ExpressTransactionStateUM.ExchangeUM -> TokenExchangeAnalyticsEvent.CexTxStatusOpened( + cryptoCurrency.symbol, + ) + is ExpressTransactionStateUM.OnrampUM -> TokenOnrampAnalyticsEvent.OnrampStatusOpened( + tokenSymbol = cryptoCurrency.symbol, + provider = expressState.providerName, + fiatCurrency = expressState.fromCurrencyCode, + ) + } + + analyticsEventsHandler.send(analyticEvent) return currentStateProvider().copy( bottomSheetConfig = TangemBottomSheetConfig( @@ -152,7 +162,6 @@ internal class ExpressStatusFactory @AssistedInject constructor( fun create( clickIntents: TokenDetailsClickIntents, appCurrencyProvider: Provider, - analyticsEventsHandlerProvider: Provider, currentStateProvider: Provider, userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt index 93bf261ff8..7d54a9eb6b 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt @@ -1,11 +1,17 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.onramp.GetOnrampStatusUseCase import com.tangem.domain.onramp.GetOnrampTransactionsUseCase import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase +import com.tangem.domain.onramp.OnrampUpdateTransactionStatusUseCase +import com.tangem.domain.onramp.model.OnrampStatus +import com.tangem.domain.onramp.model.OnrampStatus.Status.* import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM @@ -26,6 +32,8 @@ internal class OnrampStatusFactory @AssistedInject constructor( private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase, private val getOnrampStatusUseCase: GetOnrampStatusUseCase, private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase, + private val onrampUpdateTransactionStatusUseCase: OnrampUpdateTransactionStatusUseCase, + private val analyticsEventHandler: AnalyticsEventHandler, @Assisted private val currentStateProvider: Provider, @Assisted private val cryptoCurrencyStatusProvider: Provider, @Assisted private val appCurrencyProvider: Provider, @@ -40,6 +48,7 @@ internal class OnrampStatusFactory @AssistedInject constructor( cryptoCurrency = cryptoCurrency, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, appCurrencyProvider = appCurrencyProvider, + analyticsEventHandler = analyticsEventHandler, ) } @@ -79,6 +88,7 @@ internal class OnrampStatusFactory @AssistedInject constructor( onrampTx }, ifRight = { statusModel -> + sendStatusUpdateAnalytics(onrampTx, statusModel) onrampTx.copy(activeStatus = statusModel.status) }, ) @@ -90,6 +100,44 @@ internal class OnrampStatusFactory @AssistedInject constructor( .forEach { onrampRemoveTransactionUseCase(txId = it.info.txId) } } + private suspend fun sendStatusUpdateAnalytics( + onrampTx: ExpressTransactionStateUM.OnrampUM, + statusModel: OnrampStatus, + ) { + val txId = statusModel.txId + val status = toAnalyticStatus(statusModel.status) ?: return + + if (statusModel.status != onrampTx.activeStatus) { + analyticsEventHandler.send( + TokenOnrampAnalyticsEvent.OnrampStatusChanged( + tokenSymbol = cryptoCurrency.symbol, + status = status.name, + provider = onrampTx.providerName, + fiatCurrency = onrampTx.fromCurrencyCode, + ), + ) + onrampUpdateTransactionStatusUseCase(txId, statusModel.status) + } + } + + private fun toAnalyticStatus(status: OnrampStatus.Status?): ExpressAnalyticsStatus? { + return when (status) { + Expired, + Paused, + -> ExpressAnalyticsStatus.Cancelled + Created, + WaitingForPayment, + PaymentProcessing, + Paid, + Sending, + -> ExpressAnalyticsStatus.InProgress + Verifying -> ExpressAnalyticsStatus.KYC + Failed -> ExpressAnalyticsStatus.Fail + Finished -> ExpressAnalyticsStatus.Done + null -> null + } + } + @AssistedFactory interface Factory { fun create( 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 2f75b23f8a..9d0c29445d 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 @@ -164,7 +164,6 @@ internal class TokenDetailsViewModel @Inject constructor( expressStatusFactory.create( clickIntents = this, appCurrencyProvider = Provider { selectedAppCurrencyFlow.value }, - analyticsEventsHandlerProvider = Provider { analyticsEventsHandler }, currentStateProvider = Provider { uiState.value }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, userWalletId = userWalletId,