Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-21 06:35:30 -07:00
parent 3e8e14a114
commit 720d5e0cb7
9 changed files with 91 additions and 16 deletions

View file

@ -21,6 +21,7 @@ import com.tangem.features.tangempay.cashback.api.TangemPayCashbackComponent
import com.tangem.features.tangempay.navigation.TangemPayAccountDetailsInnerRoute
import com.tangem.features.tangempay.tiers.current.TangemPayCurrentPlanComponent
import com.tangem.features.tangempay.tiers.select.TangemPaySelectPlanComponent
import com.tangem.features.tangempay.tiers.select.TangemPaySelectPlanSource
import com.tangem.features.tangempay.utils.tariffPlan
import com.tangem.features.tangempay.utils.userWalletId
import com.tangem.features.tokendetails.ExpressTransactionsComponent
@ -61,8 +62,11 @@ internal class DefaultTangemPayDetailsContainerComponent @AssistedInject constru
val tariffPlan = params.initialStatus.tariffPlan
return when (params.initialRoute) {
TangemPayDetailsInitialRoute.ACCOUNT_DETAILS -> TangemPayAccountDetailsInnerRoute.AccountDetails
TangemPayDetailsInitialRoute.SELECT_PLAN -> if (tariffPlan != null) {
TangemPayAccountDetailsInnerRoute.SelectPlan(tariffPlan = tariffPlan)
TangemPayDetailsInitialRoute.TIERS_ONBOARDING -> if (tariffPlan != null) {
TangemPayAccountDetailsInnerRoute.SelectPlan(
tariffPlan = tariffPlan,
source = TangemPaySelectPlanSource.TIERS_ONBOARDING,
)
} else {
TangemPayAccountDetailsInnerRoute.AccountDetails
}
@ -117,6 +121,7 @@ internal class DefaultTangemPayDetailsContainerComponent @AssistedInject constru
params = TangemPaySelectPlanComponent.Params(
userWalletId = params.initialStatus.userWalletId,
tariffPlan = config.tariffPlan,
source = config.source,
),
)
TangemPayAccountDetailsInnerRoute.VirtualAccountDepositSuccess ->

View file

@ -3,6 +3,7 @@ package com.tangem.features.tangempay.navigation
import com.tangem.core.decompose.navigation.Route
import com.tangem.domain.models.account.TangemPayCustomerTariffPlan
import com.tangem.domain.models.pay.TangemPayCard
import com.tangem.features.tangempay.tiers.select.TangemPaySelectPlanSource
import kotlinx.serialization.Serializable
@Serializable
@ -24,6 +25,7 @@ internal sealed class TangemPayAccountDetailsInnerRoute : Route {
@Serializable
data class SelectPlan(
val tariffPlan: TangemPayCustomerTariffPlan,
val source: TangemPaySelectPlanSource,
) : TangemPayAccountDetailsInnerRoute()
@Serializable

View file

@ -1,5 +1,6 @@
package com.tangem.features.tangempay.tiers.current
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
@ -19,6 +20,8 @@ internal class TangemPayCurrentPlanComponent(
@Composable
override fun Content(modifier: Modifier) {
BackHandler { model.onBackClick() }
val state by model.state.collectAsStateWithLifecycle()
TangemPayCurrentPlanScreen(state = state, modifier = modifier)
}

View file

@ -13,21 +13,27 @@ import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.domain.models.account.TangemPayCustomerTariffPlan
import com.tangem.domain.models.account.TangemPayTariffPlan
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
import com.tangem.domain.pay.usecase.CancelTariffPlanPendingTransitionUseCase
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.navigation.TangemPayAccountDetailsInnerRoute
import com.tangem.features.tangempay.tiers.formatNextBillingDateOrNull
import com.tangem.features.tangempay.tiers.formatRecurringFeeOrNull
import com.tangem.features.tangempay.tiers.select.TangemPaySelectPlanSource
import com.tangem.features.tangempay.utils.TangemPayMessagesFactory
import com.tangem.features.tangempay.utils.tariffPlan
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("LongParameterList")
@Stable
@ModelScoped
internal class TangemPayCurrentPlanModel @Inject constructor(
@ -35,6 +41,7 @@ internal class TangemPayCurrentPlanModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val cancelPendingTransition: CancelTariffPlanPendingTransitionUseCase,
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier,
private val uiMessageSender: UiMessageSender,
private val analytics: AnalyticsEventHandler,
) : Model() {
@ -42,15 +49,37 @@ internal class TangemPayCurrentPlanModel @Inject constructor(
private val params = paramsContainer.require<TangemPayCurrentPlanComponent.Params>()
private var isProcessing: Boolean = false
private var currentPlan: TangemPayCustomerTariffPlan = params.tariffPlan
val state: StateFlow<TangemPayCurrentPlanUM>
field = MutableStateFlow(createState(params.tariffPlan))
field = MutableStateFlow(createState(currentPlan))
init {
observePlanChanges()
}
private fun observePlanChanges() {
modelScope.launch {
paymentAccountStatusSupplier(params.userWalletId)
.mapNotNull { it.tariffPlan }
.distinctUntilChanged()
.collect { plan ->
currentPlan = plan
state.value = createState(plan)
}
}
}
fun onBackClick() {
if (isProcessing) return
router.pop()
}
private fun createState(customerPlan: TangemPayCustomerTariffPlan): TangemPayCurrentPlanUM = TangemPayCurrentPlanUM(
planName = stringReference(customerPlan.plan.name),
notification = createNotification(customerPlan),
sections = buildSections(customerPlan.plan),
onBackClick = router::pop,
onBackClick = ::onBackClick,
onChangePlanClick = ::onChangePlanClick
.takeIf {
customerPlan.status != TangemPayCustomerTariffPlan.Status.DOWNGRADE_PENDING
@ -59,7 +88,12 @@ internal class TangemPayCurrentPlanModel @Inject constructor(
private fun onChangePlanClick() {
analytics.send(TangemPayAnalyticsEvents.Tiers.ChangePlanClicked())
router.push(TangemPayAccountDetailsInnerRoute.SelectPlan(params.tariffPlan))
router.push(
TangemPayAccountDetailsInnerRoute.SelectPlan(
tariffPlan = currentPlan,
source = TangemPaySelectPlanSource.CHANGE_PLAN,
),
)
}
private fun createNotification(customerPlan: TangemPayCustomerTariffPlan): TangemPayCurrentPlanUM.Notification? {
@ -95,7 +129,7 @@ internal class TangemPayCurrentPlanModel @Inject constructor(
private fun onStayOnPlanClick() {
if (isProcessing) return
val customerPlan = params.tariffPlan
val customerPlan = currentPlan
val targetPlanName = customerPlan.pendingPlan?.name ?: return
analytics.send(TangemPayAnalyticsEvents.Tiers.StayOnPlusConditionsClicked())
analytics.send(TangemPayAnalyticsEvents.Tiers.StayOnPlusPopupShowed())
@ -112,16 +146,16 @@ internal class TangemPayCurrentPlanModel @Inject constructor(
if (isProcessing) return
analytics.send(TangemPayAnalyticsEvents.Tiers.StayOnPlusPopupClicked())
isProcessing = true
state.value = createState(params.tariffPlan)
state.value = createState(currentPlan)
modelScope.launch {
cancelPendingTransition(params.userWalletId).fold(
ifRight = { router.pop() },
ifRight = {},
ifLeft = {
isProcessing = false
state.value = createState(params.tariffPlan)
state.value = createState(currentPlan)
uiMessageSender.send(message = TangemPayMessagesFactory.createGenericError())
},
)
isProcessing = false
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.features.tangempay.tiers.select
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
@ -19,6 +20,8 @@ internal class TangemPaySelectPlanComponent(
@Composable
override fun Content(modifier: Modifier) {
BackHandler { model.onBackClick() }
val state by model.state.collectAsStateWithLifecycle()
TangemPaySelectPlanScreen(state = state, modifier = modifier)
}
@ -26,5 +29,6 @@ internal class TangemPaySelectPlanComponent(
data class Params(
val userWalletId: UserWalletId,
val tariffPlan: TangemPayCustomerTariffPlan,
val source: TangemPaySelectPlanSource,
)
}

View file

@ -108,7 +108,7 @@ internal class TangemPaySelectPlanModel @Inject constructor(
state.update { buildState(showPlanCompare = false) }
}
private fun onBackClick() {
fun onBackClick() {
if (isProcessing) return
if (isConfirm) {
analytics.send(TangemPayAnalyticsEvents.Tiers.PlanChangeCancelClicked())
@ -119,6 +119,11 @@ internal class TangemPaySelectPlanModel @Inject constructor(
}
}
private fun onCloseClick() {
if (isProcessing) return
router.pop()
}
private fun onConfirmClick() {
if (isProcessing) return
@ -151,7 +156,16 @@ internal class TangemPaySelectPlanModel @Inject constructor(
state.update { buildState() }
modelScope.launch {
action().fold(
ifRight = { router.replaceAll(TangemPayAccountDetailsInnerRoute.AccountDetails) },
ifRight = {
when (params.source) {
TangemPaySelectPlanSource.TIERS_ONBOARDING -> {
router.replaceAll(TangemPayAccountDetailsInnerRoute.AccountDetails)
}
TangemPaySelectPlanSource.CHANGE_PLAN -> {
router.pop()
}
}
},
ifLeft = {
isProcessing = false
state.update { buildState() }
@ -173,7 +187,7 @@ internal class TangemPaySelectPlanModel @Inject constructor(
selectedIndex = selectedIndex,
onPlanSelected = ::onPlanSelected,
onBackClick = ::onBackClick,
onCloseClick = router::pop,
onCloseClick = ::onCloseClick,
content = if (isConfirm) buildConfirmContent() else buildSelectContent(),
compare = if (showPlanCompare) buildCompare() else null,
)

View file

@ -0,0 +1,13 @@
package com.tangem.features.tangempay.tiers.select
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
internal enum class TangemPaySelectPlanSource {
@SerialName("tiers_onboarding")
TIERS_ONBOARDING,
@SerialName("change_plan")
CHANGE_PLAN,
}

View file

@ -160,7 +160,7 @@ internal class DefaultWalletRouter @Inject constructor(
override fun openTangemPaySelectPlan(status: AccountStatus.Payment) {
val route = AppRoute.TangemPayDetails(
status = status,
initialRoute = TangemPayDetailsInitialRoute.SELECT_PLAN,
initialRoute = TangemPayDetailsInitialRoute.TIERS_ONBOARDING,
)
router.push(route)
}