From e15683a79b275c537b9bf2ad90fedfd50fe66da2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Dec 2023 18:03:41 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../di/SwapTransactionStatusStoreModule.kt | 23 +++++++++++ .../DefaultSwapTransactionStatusStore.kt | 12 ++++++ .../swaptx/SwapTransactionStatusStore.kt | 18 +++++++++ .../repository/DefaultCurrenciesRepository.kt | 1 - .../analytics/TokenExchangeAnalyticsEvent.kt | 18 ++------- features/tokendetails/impl/build.gradle.kts | 1 + ...enDetailsSwapTransactionsStateConverter.kt | 8 +--- .../viewmodels/ExchangeStatusFactory.kt | 38 ++++++++++++++++++- .../viewmodels/TokenDetailsClickIntents.kt | 3 +- .../viewmodels/TokenDetailsViewModel.kt | 23 +++-------- 10 files changed, 102 insertions(+), 43 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/SwapTransactionStatusStoreModule.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/swaptx/DefaultSwapTransactionStatusStore.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/SwapTransactionStatusStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/SwapTransactionStatusStoreModule.kt new file mode 100644 index 0000000000..25f71c60a0 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/SwapTransactionStatusStoreModule.kt @@ -0,0 +1,23 @@ +package com.tangem.datasource.di + +import com.tangem.datasource.local.datastore.RuntimeDataStore +import com.tangem.datasource.local.swaptx.DefaultSwapTransactionStatusStore +import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object SwapTransactionStatusStoreModule { + + @Provides + @Singleton + fun provideSwapTransactionStatusStore(): SwapTransactionStatusStore { + return DefaultSwapTransactionStatusStore( + dataStore = RuntimeDataStore(), + ) + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..50b4f08c9d --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/DefaultSwapTransactionStatusStore.kt @@ -0,0 +1,12 @@ +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 { + + override suspend fun getTransactionStatus(txId: String) = getSyncOrNull(txId) + + override suspend fun setTransactionStatus(txId: String, status: ExchangeAnalyticsStatus) = 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 new file mode 100644 index 0000000000..59bd30030e --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt @@ -0,0 +1,18 @@ +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 setTransactionStatus(txId: String, status: ExchangeAnalyticsStatus) +} + +enum class ExchangeAnalyticsStatus(val value: String) { + InProgress("In Progress"), + Done("Done"), + Fail("Fail"), + KYC("KYC"), + Refunded("Refunded"), +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 038d1678cf..d927d3e04e 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -399,7 +399,6 @@ internal class DefaultCurrenciesRepository( assetsStore.store(userWalletId, response.getOrThrow()) } - } catch (e: Throwable) { Timber.e(e, "Unable to fetch assets for: ${userWalletId.stringValue}") } 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 index a795ccd2af..a2161adc46 100644 --- 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 @@ -7,26 +7,16 @@ class TokenExchangeAnalyticsEvent( params: Map = mapOf(), ) : AnalyticsEvent("Token", event, params, null) { - class CexTx(token: String) : TokenScreenAnalyticsEvent( - event = "Notice - ChangeNow Swap", + class CexTxStatusOpened(token: String) : TokenScreenAnalyticsEvent( + event = "ChangeNow Status Opened", params = mapOf("Token" to token), ) - class CexTxOpened(token: String, status: String) : TokenScreenAnalyticsEvent( - event = "ChangeNow Swap Opened", + class CexTxStatusChanged(token: String, status: String) : TokenScreenAnalyticsEvent( + event = "ChangeNow Status", 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"), diff --git a/features/tokendetails/impl/build.gradle.kts b/features/tokendetails/impl/build.gradle.kts index d7592edba4..8c4c106fa0 100644 --- a/features/tokendetails/impl/build.gradle.kts +++ b/features/tokendetails/impl/build.gradle.kts @@ -50,6 +50,7 @@ dependencies { implementation(projects.core.utils) implementation(projects.core.analytics) implementation(projects.core.analytics.models) + implementation(projects.core.datasource) /** Domain modules */ implementation(projects.domain.appCurrency) 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 b67c23da81..3650c86759 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 @@ -95,7 +95,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( fromCryptoSymbol = fromCurrency.currency.symbol, fromFiatAmount = getFiatAmount(fromFiatAmount), fromCurrencyIcon = iconStateConverter.convert(fromCurrency), - onClick = { clickIntents.onSwapTransactionClick(transaction.txId, transaction.status?.status) }, + onClick = { clickIntents.onSwapTransactionClick(transaction.txId) }, onGoToProviderClick = { url -> analyticsEventsHandlerProvider().send( TokenExchangeAnalyticsEvent.GoToProviderStatus(cryptoCurrency.symbol), @@ -130,9 +130,6 @@ internal class TokenDetailsSwapTransactionsStateConverter( if (txUrl == null) return null return when (status) { ExchangeStatus.Failed -> { - analyticsEventsHandlerProvider().send( - TokenExchangeAnalyticsEvent.Fail(cryptoCurrency.symbol), - ) ExchangeStatusNotifications.Failed { analyticsEventsHandlerProvider().send( TokenExchangeAnalyticsEvent.GoToProviderFail(cryptoCurrency.symbol), @@ -141,9 +138,6 @@ internal class TokenDetailsSwapTransactionsStateConverter( } } ExchangeStatus.Verifying -> { - analyticsEventsHandlerProvider().send( - TokenExchangeAnalyticsEvent.Verification(cryptoCurrency.symbol), - ) ExchangeStatusNotifications.NeedVerification { analyticsEventsHandlerProvider().send( TokenExchangeAnalyticsEvent.GoToProviderKYC(cryptoCurrency.symbol), 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 4e7fc63875..13445dc76d 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 @@ -3,10 +3,13 @@ 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.datasource.local.swaptx.ExchangeAnalyticsStatus +import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase 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.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.feature.swap.domain.SwapRepository @@ -33,6 +36,7 @@ internal class ExchangeStatusFactory( private val swapRepository: SwapRepository, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val getMultiCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesSyncUseCase, + private val swapTransactionStatusStore: SwapTransactionStatusStore, private val dispatchers: CoroutineDispatcherProvider, private val clickIntents: TokenDetailsClickIntents, private val appCurrencyProvider: Provider, @@ -90,10 +94,26 @@ internal class ExchangeStatusFactory( return swapRepository.getExchangeStatus(txId) .fold( ifLeft = { null }, - ifRight = { it }, + ifRight = { + sendStatusUpdateAnalytics(it) + it + }, ) } + private suspend fun sendStatusUpdateAnalytics(statusModel: ExchangeStatusModel) { + val txId = statusModel.txId ?: return + val status = toAnalyticStatus(statusModel.status) ?: return + val savedStatus = swapTransactionStatusStore.getTransactionStatus(txId) + + if (savedStatus != status) { + analyticsEventsHandlerProvider().send( + TokenExchangeAnalyticsEvent.CexTxStatusChanged(cryptoCurrency.symbol, status.value), + ) + swapTransactionStatusStore.setTransactionStatus(txId, status) + } + } + private fun getExchangeStatusState( savedTransactions: List?, cryptoCurrencyStatusList: List, @@ -123,4 +143,20 @@ internal class ExchangeStatusFactory( this } } + + private fun toAnalyticStatus(status: ExchangeStatus?): ExchangeAnalyticsStatus? { + return when (status) { + ExchangeStatus.New, + ExchangeStatus.Waiting, + ExchangeStatus.Sending, + ExchangeStatus.Confirming, + ExchangeStatus.Exchanging, + -> ExchangeAnalyticsStatus.InProgress + ExchangeStatus.Verifying -> ExchangeAnalyticsStatus.KYC + ExchangeStatus.Failed -> ExchangeAnalyticsStatus.Fail + ExchangeStatus.Finished -> ExchangeAnalyticsStatus.Done + ExchangeStatus.Refunded -> ExchangeAnalyticsStatus.Refunded + else -> null + } + } } \ No newline at end of file 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 64a0d6dd2e..95093f7fdb 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,7 +2,6 @@ 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 { @@ -40,7 +39,7 @@ interface TokenDetailsClickIntents { fun onCloseRentInfoNotification() - fun onSwapTransactionClick(txId: String, status: ExchangeStatus?) + fun onSwapTransactionClick(txId: String) 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 62b23a3763..1a718c9846 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 @@ -13,6 +13,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.extensions.resourceReference +import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase @@ -36,11 +37,9 @@ 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 @@ -77,6 +76,7 @@ internal class TokenDetailsViewModel @Inject constructor( private val getMultiCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesSyncUseCase, private val swapRepository: SwapRepository, private val swapTransactionRepository: SwapTransactionRepository, + private val swapTransactionStatusStore: SwapTransactionStatusStore, private val isDemoCardUseCase: IsDemoCardUseCase, private val reduxStateHolder: ReduxStateHolder, private val analyticsEventsHandler: AnalyticsEventHandler, @@ -116,6 +116,7 @@ internal class TokenDetailsViewModel @Inject constructor( swapRepository = swapRepository, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, getMultiCryptoCurrencyStatusUseCase = getMultiCryptoCurrencyStatusUseCase, + swapTransactionStatusStore = swapTransactionStatusStore, dispatchers = dispatchers, clickIntents = this, appCurrencyProvider = Provider { selectedAppCurrencyFlow.value }, @@ -215,9 +216,6 @@ internal class TokenDetailsViewModel @Inject constructor( swapTxStatusTaskScheduler.cancelTask() exchangeStatusFactory.invoke() .onEach { swapTxs -> - if (swapTxs.isNotEmpty()) { - analyticsEventsHandler.send(TokenExchangeAnalyticsEvent.CexTx(cryptoCurrency.symbol)) - } swapTxStatusTaskScheduler.scheduleTask( viewModelScope, PeriodicTask( @@ -539,20 +537,9 @@ internal class TokenDetailsViewModel @Inject constructor( uiState = stateFactory.getStateWithRemovedRentNotification() } - override fun onSwapTransactionClick(txId: String, status: ExchangeStatus?) { + override fun onSwapTransactionClick(txId: String) { 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 - } + analyticsEventsHandler.send(TokenExchangeAnalyticsEvent.CexTxStatusOpened(cryptoCurrency.symbol)) uiState = stateFactory.getStateWithExchangeStatusBottomSheet(swapTxState) }