Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-08 13:19:07 +05:00
parent 98301bcf59
commit 2eee398c36
10 changed files with 147 additions and 7 deletions

View file

@ -125,6 +125,9 @@ internal class TangemPayCardPageScreenComponent(
dailyDepositLimit = VA_DAILY_DEPOSIT_LIMIT_PLACEHOLDER,
shouldSkipIntro = true,
listener = VirtualAccountAddFundsListener { model.bottomSheetNavigation.dismiss() },
onDetailsShown = model::onVaBankingDetailsShown,
onShareClicked = model::onVaShareDetailsClicked,
onFieldCopied = model::onVaFieldCopied,
),
)
is TangemPayCardNavigation.Receive -> tokenReceiveComponentFactory.create(

View file

@ -171,6 +171,9 @@ internal class TangemPayDetailsComponent(
dailyDepositLimit = VA_DAILY_DEPOSIT_LIMIT_PLACEHOLDER,
shouldSkipIntro = true,
listener = VirtualAccountAddFundsListener { model.bottomSheetNavigation.dismiss() },
onDetailsShown = model::onVaBankingDetailsShown,
onShareClicked = model::onVaShareDetailsClicked,
onFieldCopied = model::onVaFieldCopied,
),
)
is TangemPayDetailsNavigation.IssueAdditionalCard -> TangemPayIssueAdditionalCardComponent(

View file

@ -1,12 +1,14 @@
package com.tangem.features.tangempay.model
import androidx.compose.runtime.Stable
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.models.ReceiveAddressModel
import com.tangem.domain.models.ReceiveAddressModel.DisplayType
import com.tangem.domain.pay.model.TangemPayTopUpData
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayAddFundsComponent
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
@ -21,13 +23,22 @@ internal class TangemPayAddFundsModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val virtualAccountToggles: VirtualAccountFeatureToggles,
virtualAccountToggles: VirtualAccountFeatureToggles,
analytics: AnalyticsEventHandler,
) : Model() {
private val params = paramsContainer.require<TangemPayAddFundsComponent.Params>()
private val isBankTransferShown = virtualAccountToggles.isVaMvp0Enabled && params.virtualAccountOnramp != null
val uiState: TangemPayAddFundsUM = getInitialState()
init {
if (isBankTransferShown) {
analytics.send(TangemPayAnalyticsEvents.VaTopupButtonShowed())
}
}
private fun getInitialState(): TangemPayAddFundsUM {
val data = TangemPayTopUpData(
currency = params.cryptoCurrency,
@ -45,7 +56,7 @@ internal class TangemPayAddFundsModel @Inject constructor(
return TangemPayAddFundsUMConverter(
listener = params.listener,
isRedesignEnabled = tangemPayFeatureToggles.isRedesignEnabled,
shouldShowBankTransfer = virtualAccountToggles.isVaMvp0Enabled && params.virtualAccountOnramp != null,
shouldShowBankTransfer = isBankTransferShown,
).convert(data)
}

View file

@ -489,6 +489,7 @@ internal class TangemPayCardPageModel @Inject constructor(
override fun onClickBankTransfer() {
val loaded = currentStatus.value.ifLoadedOrNull { it } ?: return
val onramp = loaded.virtualAccount ?: return
analytics.send(TangemPayAnalyticsEvents.VaTopupButtonClicked())
bottomSheetNavigation.dismiss()
bottomSheetNavigation.activate(
TangemPayCardNavigation.VirtualAccountDeposit(
@ -500,6 +501,7 @@ internal class TangemPayCardPageModel @Inject constructor(
}
fun onVirtualAccountOrderCreated() {
analytics.send(TangemPayAnalyticsEvents.VaSuccessScreenActivation())
bottomSheetNavigation.dismiss()
router.push(TangemPayCardDetailsInnerRoute.VirtualAccountDepositSuccess)
}
@ -514,6 +516,18 @@ internal class TangemPayCardPageModel @Inject constructor(
)
}
fun onVaBankingDetailsShown() {
analytics.send(TangemPayAnalyticsEvents.VaBankingDetailsShowed())
}
fun onVaShareDetailsClicked() {
analytics.send(TangemPayAnalyticsEvents.VaShareDetailsButtonClicked())
}
fun onVaFieldCopied(field: String) {
analytics.send(TangemPayAnalyticsEvents.VaCopyFieldClicked(field))
}
override fun onDismissAddFunds() {
bottomSheetNavigation.dismiss()
}

View file

@ -367,6 +367,7 @@ internal class TangemPayDetailsModel @Inject constructor(
override fun onClickBankTransfer() {
val loaded = currentStatus.value.ifLoadedOrNull { it } ?: return
val onramp = loaded.virtualAccount ?: return
analytics.send(TangemPayAnalyticsEvents.VaTopupButtonClicked())
bottomSheetNavigation.dismiss()
bottomSheetNavigation.activate(
TangemPayDetailsNavigation.VirtualAccountDeposit(
@ -378,6 +379,7 @@ internal class TangemPayDetailsModel @Inject constructor(
}
fun onVirtualAccountOrderCreated() {
analytics.send(TangemPayAnalyticsEvents.VaSuccessScreenActivation())
bottomSheetNavigation.dismiss()
router.push(TangemPayAccountDetailsInnerRoute.VirtualAccountDepositSuccess)
}
@ -392,6 +394,18 @@ internal class TangemPayDetailsModel @Inject constructor(
)
}
fun onVaBankingDetailsShown() {
analytics.send(TangemPayAnalyticsEvents.VaBankingDetailsShowed())
}
fun onVaShareDetailsClicked() {
analytics.send(TangemPayAnalyticsEvents.VaShareDetailsButtonClicked())
}
fun onVaFieldCopied(field: String) {
analytics.send(TangemPayAnalyticsEvents.VaCopyFieldClicked(field))
}
override fun onClickReceive(data: TangemPayTopUpData) {
analytics.send(TangemPayAnalyticsEvents.ReceiveFundsClicked())
bottomSheetNavigation.dismiss()

View file

@ -1,6 +1,7 @@
package com.tangem.features.tangempay.model
import androidx.compose.runtime.Stable
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@ -10,6 +11,7 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.ToastMessage
import com.tangem.domain.models.account.VirtualAccountOnramp
import com.tangem.domain.pay.usecase.CreateVirtualAccountOrderUseCase
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.components.TangemPayVirtualAccountDepositComponent
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayVirtualAccountDepositUM
@ -29,6 +31,7 @@ internal class TangemPayVirtualAccountDepositModel @Inject constructor(
private val urlOpener: UrlOpener,
private val uiMessageSender: UiMessageSender,
private val createVirtualAccountOrderUseCase: CreateVirtualAccountOrderUseCase,
private val analytics: AnalyticsEventHandler,
) : Model() {
private val params = paramsContainer.require<TangemPayVirtualAccountDepositComponent.Params>()
@ -55,14 +58,29 @@ internal class TangemPayVirtualAccountDepositModel @Inject constructor(
),
)
init {
val event = if (params.virtualAccountOnramp is VirtualAccountOnramp.Eligible) {
TangemPayAnalyticsEvents.VaConditionsPopupShowedFirstTime()
} else {
TangemPayAnalyticsEvents.VaConditionsPopupShowed()
}
analytics.send(event)
}
fun onDismiss() {
params.onDismiss()
}
private fun onShowDetailsClick() {
when (params.virtualAccountOnramp) {
is VirtualAccountOnramp.Available -> params.onShowDetails(params.virtualAccountOnramp)
VirtualAccountOnramp.Eligible -> createVirtualAccountOrder()
is VirtualAccountOnramp.Available -> {
analytics.send(TangemPayAnalyticsEvents.VaShowDetailsClicked())
params.onShowDetails(params.virtualAccountOnramp)
}
VirtualAccountOnramp.Eligible -> {
analytics.send(TangemPayAnalyticsEvents.VaShowDetailsFirstTimeClicked())
createVirtualAccountOrder()
}
}
}

View file

@ -4,6 +4,7 @@ import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.google.common.truth.Truth.assertThat
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.model.MutableParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.url.UrlOpener
@ -12,6 +13,7 @@ import com.tangem.domain.models.account.BankCredentials
import com.tangem.domain.models.account.VirtualAccountOnramp
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.usecase.CreateVirtualAccountOrderUseCase
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.features.tangempay.components.TangemPayVirtualAccountDepositComponent
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
@ -39,10 +41,11 @@ internal class TangemPayVirtualAccountDepositModelTest {
private val createVirtualAccountOrderUseCase: CreateVirtualAccountOrderUseCase = mockk()
private val onShowDetails: (VirtualAccountOnramp.Available) -> Unit = mockk(relaxed = true)
private val onOrderCreated: () -> Unit = mockk(relaxed = true)
private val analytics: AnalyticsEventHandler = mockk(relaxed = true)
@BeforeEach
fun resetMocks() {
clearMocks(createVirtualAccountOrderUseCase, onShowDetails, onOrderCreated, uiMessageSender)
clearMocks(createVirtualAccountOrderUseCase, onShowDetails, onOrderCreated, uiMessageSender, analytics)
}
@Test
@ -58,6 +61,8 @@ internal class TangemPayVirtualAccountDepositModelTest {
// Assert
verify(exactly = 1) { onShowDetails(onramp) }
coVerify(exactly = 0) { createVirtualAccountOrderUseCase(any(), any()) }
verify(exactly = 1) { analytics.send(ofType<TangemPayAnalyticsEvents.VaConditionsPopupShowed>()) }
verify(exactly = 1) { analytics.send(ofType<TangemPayAnalyticsEvents.VaShowDetailsClicked>()) }
}
@Test
@ -74,6 +79,8 @@ internal class TangemPayVirtualAccountDepositModelTest {
coVerify(exactly = 1) { createVirtualAccountOrderUseCase(userWalletId, paymentAccountAddress) }
verify(exactly = 1) { onOrderCreated() }
assertThat(model.uiState.value.isLoading).isFalse()
verify(exactly = 1) { analytics.send(ofType<TangemPayAnalyticsEvents.VaConditionsPopupShowedFirstTime>()) }
verify(exactly = 1) { analytics.send(ofType<TangemPayAnalyticsEvents.VaShowDetailsFirstTimeClicked>()) }
}
@Test
@ -130,6 +137,7 @@ internal class TangemPayVirtualAccountDepositModelTest {
urlOpener = urlOpener,
uiMessageSender = uiMessageSender,
createVirtualAccountOrderUseCase = createVirtualAccountOrderUseCase,
analytics = analytics,
)
private fun bankCredentials() = BankCredentials(