Updated on 2026-08-14
This commit is contained in:
parent
d71d29b72b
commit
b750588ef9
21 changed files with 277 additions and 67 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<ExchangeAnalyticsStatus>,
|
||||
) : SwapTransactionStatusStore, StringKeyDataStore<ExchangeAnalyticsStatus> by dataStore {
|
||||
private val dataStore: StringKeyDataStore<ExpressAnalyticsStatus>,
|
||||
) : SwapTransactionStatusStore, StringKeyDataStore<ExpressAnalyticsStatus> 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)
|
||||
}
|
||||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ internal class TransactionConverter : TwoWayConverter<OnrampTransactionDTO, Onra
|
|||
providerImageUrl = value.providerImageUrl,
|
||||
providerType = value.providerType,
|
||||
redirectUrl = "", // not necessary field
|
||||
paymentMethod = value.paymentMethod,
|
||||
residency = value.residency,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -44,6 +46,8 @@ internal class TransactionConverter : TwoWayConverter<OnrampTransactionDTO, Onra
|
|||
providerName = value.providerName,
|
||||
providerImageUrl = value.providerImageUrl,
|
||||
providerType = value.providerType,
|
||||
paymentMethod = value.paymentMethod,
|
||||
residency = value.residency,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -52,4 +52,8 @@ data class OnrampTransactionDTO(
|
|||
val providerImageUrl: String, // todo onramp fix after SwapProvider moved to own module
|
||||
@Json(name = "providerType")
|
||||
val providerType: String, // todo onramp fix after SwapProvider moved to own module
|
||||
@Json(name = "paymentMethod")
|
||||
val paymentMethod: String,
|
||||
@Json(name = "residency")
|
||||
val residency: String,
|
||||
)
|
||||
|
|
@ -21,6 +21,8 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
* @property providerName Provider name
|
||||
* @property providerImageUrl Provider image link
|
||||
* @property providerType Provider type
|
||||
* @property paymentMethod Transaction payment method
|
||||
* @property paymentMethod Residency country
|
||||
*/
|
||||
data class OnrampTransaction(
|
||||
val txId: String,
|
||||
|
|
@ -37,4 +39,6 @@ data class OnrampTransaction(
|
|||
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 redirectUrl: String,
|
||||
val paymentMethod: String,
|
||||
val residency: String,
|
||||
)
|
||||
|
|
@ -5,19 +5,15 @@ import com.tangem.domain.onramp.model.OnrampError
|
|||
import com.tangem.domain.onramp.model.OnrampStatus
|
||||
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||
import com.tangem.domain.onramp.repositories.OnrampTransactionRepository
|
||||
|
||||
class GetOnrampStatusUseCase(
|
||||
private val onrampRepository: OnrampRepository,
|
||||
private val onrampTransactionRepository: OnrampTransactionRepository,
|
||||
private val errorResolver: OnrampErrorResolver,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(txId: String): Either<OnrampError, OnrampStatus> {
|
||||
return Either.catch {
|
||||
val status = onrampRepository.getStatus(txId)
|
||||
onrampTransactionRepository.updateTransactionStatus(txId = txId, status = status.status)
|
||||
status
|
||||
onrampRepository.getStatus(txId)
|
||||
}.mapLeft {
|
||||
errorResolver.resolve(it)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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<String, String> = 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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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<OnrampMainComponentUM> get() = _state.asStateFlow()
|
||||
val bottomSheetNavigation: SlotNavigation<OnrampMainBottomSheetConfig> = 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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<CryptoCurrencyStatus?>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Converter<OnrampTransaction, ExpressTransactionStateUM.OnrampUM> {
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ import java.util.Locale
|
|||
internal class TokenDetailsSwapTransactionsStateConverter(
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
private val cryptoCurrency: CryptoCurrency,
|
||||
private val analyticsEventsHandlerProvider: Provider<AnalyticsEventHandler>,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
appCurrencyProvider: Provider<AppCurrency>,
|
||||
) : Converter<Unit, PersistentList<ExpressTransactionStateUM.ExchangeUM>> {
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<AppCurrency>,
|
||||
@Assisted private val analyticsEventsHandlerProvider: Provider<AnalyticsEventHandler>,
|
||||
@Assisted private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
@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<AppCurrency>,
|
||||
analyticsEventsHandlerProvider: Provider<AnalyticsEventHandler>,
|
||||
currentStateProvider: Provider<TokenDetailsState>,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
|
|
|
|||
|
|
@ -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<AppCurrency>,
|
||||
@Assisted analyticsEventsHandlerProvider: Provider<AnalyticsEventHandler>,
|
||||
@Assisted userWalletId: UserWalletId,
|
||||
@Assisted cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||
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<AppCurrency>,
|
||||
analyticsEventsHandlerProvider: Provider<AnalyticsEventHandler>,
|
||||
currentStateProvider: Provider<TokenDetailsState>,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
|
|
|
|||
|
|
@ -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<TokenDetailsState>,
|
||||
@Assisted private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||
@Assisted private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue