Updated on 2026-08-14
This commit is contained in:
parent
8e9417d04c
commit
ee846a2738
10 changed files with 147 additions and 7 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -154,6 +154,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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -314,6 +314,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(
|
||||
|
|
@ -325,6 +326,7 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
fun onVirtualAccountOrderCreated() {
|
||||
analytics.send(TangemPayAnalyticsEvents.VaSuccessScreenActivation())
|
||||
bottomSheetNavigation.dismiss()
|
||||
router.push(TangemPayAccountDetailsInnerRoute.VirtualAccountDepositSuccess)
|
||||
}
|
||||
|
|
@ -339,6 +341,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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ interface VirtualAccountAddFundsBottomSheetComponent : ComposableBottomSheetComp
|
|||
val dailyDepositLimit: String,
|
||||
val listener: VirtualAccountAddFundsListener,
|
||||
val shouldSkipIntro: Boolean = false,
|
||||
// Analytics hooks — supplied by callers that track this sheet (e.g. TangemPay VA topup); no-op otherwise.
|
||||
val onDetailsShown: () -> Unit = {},
|
||||
val onShareClicked: () -> Unit = {},
|
||||
val onFieldCopied: (fieldName: String) -> Unit = {},
|
||||
)
|
||||
|
||||
data class RequisitesRow(
|
||||
|
|
|
|||
|
|
@ -35,6 +35,13 @@ internal class VirtualAccountAddFundsModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
init {
|
||||
if (params.shouldSkipIntro) {
|
||||
// send analytics
|
||||
params.onDetailsShown()
|
||||
}
|
||||
}
|
||||
|
||||
fun onDismiss() {
|
||||
params.listener.onAddFundsDismiss()
|
||||
}
|
||||
|
|
@ -44,6 +51,7 @@ internal class VirtualAccountAddFundsModel @Inject constructor(
|
|||
)
|
||||
|
||||
private fun showDetailsContent() {
|
||||
params.onDetailsShown()
|
||||
uiState.update { state -> state.copy(content = buildDetailsContent()) }
|
||||
}
|
||||
|
||||
|
|
@ -52,13 +60,19 @@ internal class VirtualAccountAddFundsModel @Inject constructor(
|
|||
.map { detailItem(label = it.title, value = it.value) }
|
||||
.toImmutableList(),
|
||||
dailyLimit = params.dailyDepositLimit,
|
||||
onShareClick = { shareManager.shareText(buildShareText()) },
|
||||
onShareClick = {
|
||||
params.onShareClicked()
|
||||
shareManager.shareText(buildShareText())
|
||||
},
|
||||
)
|
||||
|
||||
private fun detailItem(label: String, value: String) = VirtualAccountAddFundsUM.DetailItem(
|
||||
label = stringReference(label),
|
||||
value = value,
|
||||
onCopyClick = { clipboardManager.setText(text = value, isSensitive = true) },
|
||||
onCopyClick = {
|
||||
params.onFieldCopied(label)
|
||||
clipboardManager.setText(text = value, isSensitive = true)
|
||||
},
|
||||
)
|
||||
|
||||
private fun buildShareText(): String {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue