Updated on 2026-08-14
This commit is contained in:
parent
e1aa83f8b7
commit
e15683a79b
10 changed files with 102 additions and 43 deletions
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ExchangeAnalyticsStatus>,
|
||||
) : SwapTransactionStatusStore, StringKeyDataStore<ExchangeAnalyticsStatus> by dataStore {
|
||||
|
||||
override suspend fun getTransactionStatus(txId: String) = getSyncOrNull(txId)
|
||||
|
||||
override suspend fun setTransactionStatus(txId: String, status: ExchangeAnalyticsStatus) = store(txId, status)
|
||||
}
|
||||
|
|
@ -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"),
|
||||
}
|
||||
|
|
@ -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}")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,26 +7,16 @@ class TokenExchangeAnalyticsEvent(
|
|||
params: Map<String, String> = 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"),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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<AppCurrency>,
|
||||
|
|
@ -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<SavedSwapTransactionListModel>?,
|
||||
cryptoCurrencyStatusList: List<CryptoCurrencyStatus>,
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue