Updated on 2026-08-14
This commit is contained in:
parent
c716d80696
commit
ae9eb162f6
18 changed files with 269 additions and 94 deletions
|
|
@ -1,5 +1,21 @@
|
|||
package com.tangem.features.tangempay
|
||||
|
||||
import com.tangem.utils.SupportedLanguages
|
||||
|
||||
object TangemPayConstants {
|
||||
const val TERMS_AND_LIMITS_LINK = "https://tangem.com/docs/en/tangem-visa-tariffs.pdf"
|
||||
|
||||
fun visaBenefitsLink(): String {
|
||||
val lang = SupportedLanguages.getCurrentSupportedLanguageCode()
|
||||
val segment = when (lang) {
|
||||
"es" -> "es"
|
||||
"pt" -> "pt"
|
||||
"ja" -> "ja"
|
||||
"zh" -> "zh-Hans"
|
||||
"fr" -> "fr"
|
||||
"de" -> "de"
|
||||
else -> "en"
|
||||
}
|
||||
return "https://tangem.com/$segment/tangem-pay/visa-benefits/"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
import com.tangem.domain.models.account.TangemPayCustomerTariffPlan
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlanState
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.tiers.formatNextBillingDateOrNull
|
||||
import com.tangem.features.tangempay.tiers.formatRecurringFeeOrNull
|
||||
import com.tangem.features.tangempay.utils.TangemPayDetailIntents
|
||||
|
||||
internal class TangemPayDetailsNotificationFactory(
|
||||
private val intents: TangemPayDetailIntents,
|
||||
private val isRemoveAccountEnabled: Boolean,
|
||||
private val isTiersPlusPlanEnabled: Boolean,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
) {
|
||||
fun createErrorConfig(error: PaymentAccountStatusValue.Error?): NotificationConfig? = when (error) {
|
||||
null -> null
|
||||
PaymentAccountStatusValue.Error.NotSynced -> createRenewSessionNotificationConfig(isRedesignEnabled)
|
||||
else -> createAccountUnavailableConfig(isRedesignEnabled)
|
||||
}
|
||||
|
||||
fun createAccountDeactivatedConfig(isRedesignEnabled: Boolean) = NotificationConfig(
|
||||
title = resourceReference(R.string.tangempay_account_deactivated_message_title),
|
||||
subtitle = resourceReference(R.string.tangempay_account_deactivated_message_subtitle),
|
||||
iconResId = if (isRedesignEnabled) R.drawable.ic_alert_circle_24 else R.drawable.img_attention_20,
|
||||
buttonsState = if (isRemoveAccountEnabled) {
|
||||
NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.tangempay_remove_account),
|
||||
onClick = intents::onRemoveAccount,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
|
||||
// TODO v_rodionov: strings hardcoded for now - wait for localization
|
||||
fun createAwaitingDepositConfig(tariffPlan: TangemPayTariffPlanState?): NotificationConfig? {
|
||||
if (!isTiersPlusPlanEnabled) return null
|
||||
if (tariffPlan == null) return null
|
||||
|
||||
if (tariffPlan.tariff.status == TangemPayCustomerTariffPlan.Status.SYSTEM_DOWNGRADE_PENDING) {
|
||||
return createTariffSystemDownGradePendingConfig(tariffPlan)
|
||||
}
|
||||
|
||||
val order = tariffPlan.order ?: return null
|
||||
val orderStep = order.step
|
||||
if (orderStep !is TangemPayTariffPlanState.OrderStep.AwaitingDeposit) return null
|
||||
|
||||
val feeText = orderStep.toPlan.formatRecurringFeeOrNull() ?: return null
|
||||
|
||||
val title = "Top-up your account on $feeText"
|
||||
return NotificationConfig(
|
||||
title = stringReference(title),
|
||||
subtitle = stringReference("To pay monthly fee for plan and start use card"),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = stringReference("Cancel ${orderStep.toPlan.name}, move to ${orderStep.fromPlan.name}"),
|
||||
onClick = { intents.onCancelPlusTransition(order.orderId) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createRenewSessionNotificationConfig(isRedesignEnabled: Boolean) = NotificationConfig(
|
||||
title = resourceReference(R.string.tangempay_sync_needed_title),
|
||||
subtitle = resourceReference(R.string.tangempay_sync_needed_body),
|
||||
iconResId = if (isRedesignEnabled) 0 else R.drawable.img_attention_20,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.tangempay_sync_needed_button),
|
||||
onClick = intents::onRenewSession,
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
),
|
||||
)
|
||||
|
||||
private fun createAccountUnavailableConfig(isRedesignEnabled: Boolean) = NotificationConfig(
|
||||
title = resourceReference(R.string.tangempay_temporarily_unavailable),
|
||||
subtitle = resourceReference(R.string.tangempay_service_unreachable_try_later),
|
||||
iconResId = if (isRedesignEnabled) R.drawable.ic_alert_circle_24 else R.drawable.img_attention_20,
|
||||
)
|
||||
|
||||
// TODO v_rodionov: strings hardcoded for now - wait for localization
|
||||
private fun createTariffSystemDownGradePendingConfig(tariffPlan: TangemPayTariffPlanState): NotificationConfig? {
|
||||
val date = tariffPlan.tariff.formatNextBillingDateOrNull() ?: return null
|
||||
val planName = tariffPlan.tariff.plan.name
|
||||
return NotificationConfig(
|
||||
title = stringReference("Top up your account shortly"),
|
||||
subtitle = stringReference("If it will remain below zero your $planName cards will be closed on $date"),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = stringReference("Add funds"),
|
||||
onClick = intents::onClickAddFunds,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,19 +3,15 @@ package com.tangem.features.tangempay.entity
|
|||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.themedColor
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.getJavaCurrencyByCode
|
||||
import com.tangem.core.ui.format.bigdecimal.optionalDecimals
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.generated.icons.Icons
|
||||
import com.tangem.core.ui.res.generated.icons.ic_document_20
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
import com.tangem.domain.models.account.TangemPayCustomerTariffPlan
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlan
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlanState
|
||||
import com.tangem.domain.models.pay.TangemPayCard
|
||||
|
|
@ -41,6 +37,13 @@ internal class TangemPayDetailsStateFactory(
|
|||
private val isMultipleCardsEnabled: Boolean,
|
||||
private val isTiersPlusPlanEnabled: Boolean,
|
||||
) {
|
||||
private val notificationFactory = TangemPayDetailsNotificationFactory(
|
||||
intents = intents,
|
||||
isRemoveAccountEnabled = isRemoveAccountEnabled,
|
||||
isTiersPlusPlanEnabled = isTiersPlusPlanEnabled,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
)
|
||||
|
||||
fun getLoadingState(): TangemPayDetailsUM {
|
||||
return TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(
|
||||
|
|
@ -75,6 +78,8 @@ internal class TangemPayDetailsStateFactory(
|
|||
val isAddCardEnabled = isFresh && !hasIssuingCard
|
||||
val areActionButtonsEnabled = isFresh && hasUnfrozenCard
|
||||
val hasWithdrawableBalance = status.balance.hasWithdrawableAmount
|
||||
val errorNotification = notificationFactory.createErrorConfig(status.error)
|
||||
val awaitingDepositNotification = notificationFactory.createAwaitingDepositConfig(status.tariffPlan)
|
||||
return TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(
|
||||
onBackClick = onBack,
|
||||
|
|
@ -111,17 +116,11 @@ internal class TangemPayDetailsStateFactory(
|
|||
),
|
||||
isBalanceHidden = false,
|
||||
addToWalletBlockState = null,
|
||||
errorNotificationConfig = createErrorConfig(status.error) ?: createAwaitingDepositConfig(status.tariffPlan),
|
||||
errorNotificationConfig = errorNotification ?: awaitingDepositNotification,
|
||||
accountDeactivatedNotificationConfig = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createErrorConfig(error: PaymentAccountStatusValue.Error?): NotificationConfig? = when (error) {
|
||||
null -> null
|
||||
PaymentAccountStatusValue.Error.NotSynced -> createRenewSessionNotificationConfig(isRedesignEnabled)
|
||||
else -> createAccountUnavailableConfig(isRedesignEnabled)
|
||||
}
|
||||
|
||||
private fun List<TangemPayCard>.resolveProgressBanner(): CardsProgressBannerUM? = when {
|
||||
any { it.state == TangemPayCardState.Reissuing } -> CardsProgressBannerUM.Reissuing
|
||||
any { it.state == TangemPayCardState.Issuing } -> CardsProgressBannerUM.Issuing
|
||||
|
|
@ -129,6 +128,7 @@ internal class TangemPayDetailsStateFactory(
|
|||
}
|
||||
|
||||
fun getDeactivatedState(hasWithdrawableBalance: Boolean): TangemPayDetailsUM {
|
||||
val accountDeactivatedNotification = notificationFactory.createAccountDeactivatedConfig(isRedesignEnabled)
|
||||
return TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(
|
||||
onBackClick = onBack,
|
||||
|
|
@ -150,65 +150,10 @@ internal class TangemPayDetailsStateFactory(
|
|||
isBalanceHidden = false,
|
||||
addToWalletBlockState = null,
|
||||
errorNotificationConfig = null,
|
||||
accountDeactivatedNotificationConfig = createAccountDeactivatedConfig(isRedesignEnabled),
|
||||
accountDeactivatedNotificationConfig = accountDeactivatedNotification,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createAccountUnavailableConfig(isRedesignEnabled: Boolean) = NotificationConfig(
|
||||
title = resourceReference(R.string.tangempay_temporarily_unavailable),
|
||||
subtitle = resourceReference(R.string.tangempay_service_unreachable_try_later),
|
||||
iconResId = if (isRedesignEnabled) R.drawable.ic_alert_circle_24 else R.drawable.img_attention_20,
|
||||
)
|
||||
|
||||
private fun createAccountDeactivatedConfig(isRedesignEnabled: Boolean) = NotificationConfig(
|
||||
title = resourceReference(R.string.tangempay_account_deactivated_message_title),
|
||||
subtitle = resourceReference(R.string.tangempay_account_deactivated_message_subtitle),
|
||||
iconResId = if (isRedesignEnabled) R.drawable.ic_alert_circle_24 else R.drawable.img_attention_20,
|
||||
buttonsState = if (isRemoveAccountEnabled) {
|
||||
NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.tangempay_remove_account),
|
||||
onClick = intents::onRemoveAccount,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
|
||||
// TODO v_rodionov: strings hardcoded for now - wait for localization
|
||||
private fun createAwaitingDepositConfig(tariffPlan: TangemPayTariffPlanState?): NotificationConfig? {
|
||||
val order = tariffPlan?.order ?: return null
|
||||
|
||||
val orderStep = order.step
|
||||
if (orderStep !is TangemPayTariffPlanState.OrderStep.AwaitingDeposit) return null
|
||||
|
||||
val recurringFee = orderStep.toPlan.fees.find { it.type == TangemPayTariffPlan.Fee.Type.RECURRING }
|
||||
val feeText = recurringFee?.let { fee ->
|
||||
val currency = getJavaCurrencyByCode(fee.currency)
|
||||
fee.amount.format { fiat(currency.currencyCode, currency.symbol).optionalDecimals() }
|
||||
}
|
||||
val title = if (feeText != null) "Top-up your account on $feeText" else "Top-up your account"
|
||||
return NotificationConfig(
|
||||
title = stringReference(title),
|
||||
subtitle = stringReference("To pay monthly fee for plan and start use card"),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = stringReference("Cancel ${orderStep.toPlan.name}, move to ${orderStep.fromPlan.name}"),
|
||||
onClick = { intents.onCancelPlusTransition(order.orderId) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createRenewSessionNotificationConfig(isRedesignEnabled: Boolean) = NotificationConfig(
|
||||
title = resourceReference(R.string.tangempay_sync_needed_title),
|
||||
subtitle = resourceReference(R.string.tangempay_sync_needed_body),
|
||||
iconResId = if (isRedesignEnabled) 0 else R.drawable.img_attention_20,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.tangempay_sync_needed_button),
|
||||
onClick = intents::onRenewSession,
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
),
|
||||
)
|
||||
|
||||
private fun getTopBarMenuItems(): ImmutableList<TangemDropdownMenuItem> {
|
||||
return persistentListOf(
|
||||
TangemDropdownMenuItem(
|
||||
|
|
@ -285,6 +230,23 @@ internal class TangemPayDetailsStateFactory(
|
|||
tintReference = { TangemTheme.colors3.icon.primary },
|
||||
),
|
||||
subtitle = stringReference(tariffPlan.tariff.plan.name),
|
||||
isEnabled = tariffPlan.order?.step !is TangemPayTariffPlanState.OrderStep.AwaitingDeposit &&
|
||||
tariffPlan.tariff.status != TangemPayCustomerTariffPlan.Status.TRANSITIONING,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (isTiersPlusPlanEnabled &&
|
||||
tariffPlan != null &&
|
||||
tariffPlan.tariff.plan.type != TangemPayTariffPlan.Type.BASIC
|
||||
) {
|
||||
add(
|
||||
TangemPayDropDownItemUM(
|
||||
title = resourceReference(R.string.tangempay_visa_benefits),
|
||||
onClick = intents::onClickVisaBenefits,
|
||||
icon = TangemIconUM.Icon(
|
||||
iconRes = CoreUiR.drawable.ic_heart_20,
|
||||
tintReference = { TangemTheme.colors3.icon.primary },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ internal sealed class TangemPayDetailsBalanceBlockState {
|
|||
override val cardsBlockState: CardsBlockState?,
|
||||
val fiatBalance: TextReference,
|
||||
val isBalanceFlickering: Boolean,
|
||||
val isNegative: Boolean,
|
||||
val isMuted: Boolean = false,
|
||||
) : TangemPayDetailsBalanceBlockState()
|
||||
|
||||
|
|
|
|||
|
|
@ -173,11 +173,13 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onCancelPlusTransition(orderId: String) {
|
||||
uiState.update(TangemPayErrorNotificationTransformer(shouldShowProgress = true))
|
||||
modelScope.launch {
|
||||
cancelTangemPayOrderUseCase(userWalletId = userWalletId, orderId = orderId)
|
||||
.onLeft {
|
||||
uiMessageSender.send(TangemPayMessagesFactory.createGenericError())
|
||||
}
|
||||
uiState.update(TangemPayErrorNotificationTransformer(shouldShowProgress = false))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -444,6 +446,10 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
urlOpener.openUrl(TangemPayConstants.TERMS_AND_LIMITS_LINK)
|
||||
}
|
||||
|
||||
override fun onClickVisaBenefits() {
|
||||
urlOpener.openUrl(TangemPayConstants.visaBenefitsLink())
|
||||
}
|
||||
|
||||
override fun onClickCurrentPlan(tariffPlan: TangemPayCustomerTariffPlan) {
|
||||
router.push(TangemPayAccountDetailsInnerRoute.CurrentPlan(tariffPlan))
|
||||
}
|
||||
|
|
@ -504,16 +510,16 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onRenewSession() {
|
||||
uiState.update(TangemPayRenewSessionTransformer(shouldShowProgress = true))
|
||||
uiState.update(TangemPayErrorNotificationTransformer(shouldShowProgress = true))
|
||||
modelScope.launch {
|
||||
produceTangemPayInitialDataUseCase(userWalletId)
|
||||
.onRight {
|
||||
paymentAccountStatusFetcher.invoke(userWalletId)
|
||||
uiState.update(TangemPayRenewSessionTransformer(shouldShowProgress = false))
|
||||
uiState.update(TangemPayErrorNotificationTransformer(shouldShowProgress = false))
|
||||
}
|
||||
.onLeft {
|
||||
uiMessageSender.send(SnackbarMessage(resourceReference(R.string.common_error)))
|
||||
uiState.update(TangemPayRenewSessionTransformer(shouldShowProgress = false))
|
||||
uiState.update(TangemPayErrorNotificationTransformer(shouldShowProgress = false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ internal class DetailsBalanceTransformer(
|
|||
actionButtons = prevState.balanceBlockState.actionButtons,
|
||||
cardsBlockState = prevState.balanceBlockState.cardsBlockState,
|
||||
isMuted = isMuted,
|
||||
isNegative = fiatBalance.availableBalance.signum() < 0,
|
||||
)
|
||||
return prevState.copy(balanceBlockState = balance)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.core.ui.components.notifications.NotificationConfig
|
|||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class TangemPayRenewSessionTransformer(
|
||||
internal class TangemPayErrorNotificationTransformer(
|
||||
private val shouldShowProgress: Boolean,
|
||||
) : Transformer<TangemPayDetailsUM> {
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.features.tangempay.tiers
|
||||
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.getJavaCurrencyByCode
|
||||
import com.tangem.core.ui.format.bigdecimal.optionalDecimals
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.domain.models.account.TangemPayCustomerTariffPlan
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlan
|
||||
import org.joda.time.format.DateTimeFormatter
|
||||
|
||||
fun TangemPayTariffPlan.formatRecurringFeeOrNull(): String? {
|
||||
val fee = fees.find { it.type == TangemPayTariffPlan.Fee.Type.RECURRING } ?: return null
|
||||
val currency = getJavaCurrencyByCode(fee.currency)
|
||||
return fee.amount.format { fiat(currency.currencyCode, currency.symbol).optionalDecimals() }
|
||||
}
|
||||
|
||||
fun TangemPayCustomerTariffPlan.formatNextBillingDateOrNull(
|
||||
formatter: DateTimeFormatter = DateTimeFormatters.dateMMMMdYYYY,
|
||||
): String? {
|
||||
return nextBillingAt?.let { DateTimeFormatters.formatDate(it, formatter) }
|
||||
}
|
||||
|
|
@ -7,9 +7,13 @@ import com.tangem.core.decompose.model.ParamsContainer
|
|||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.domain.models.account.TangemPayCustomerTariffPlan
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlan
|
||||
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.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -28,16 +32,41 @@ internal class TangemPayCurrentPlanModel @Inject constructor(
|
|||
private val params = paramsContainer.require<TangemPayCurrentPlanComponent.Params>()
|
||||
|
||||
val state: StateFlow<TangemPayCurrentPlanUM>
|
||||
field = MutableStateFlow(createState(params.tariffPlan.plan))
|
||||
field = MutableStateFlow(createState(params.tariffPlan))
|
||||
|
||||
private fun createState(plan: TangemPayTariffPlan): TangemPayCurrentPlanUM = TangemPayCurrentPlanUM(
|
||||
planName = stringReference(plan.name),
|
||||
notification = null,
|
||||
sections = buildSections(plan),
|
||||
private fun createState(customerPlan: TangemPayCustomerTariffPlan): TangemPayCurrentPlanUM = TangemPayCurrentPlanUM(
|
||||
planName = stringReference(customerPlan.plan.name),
|
||||
notification = createNotification(customerPlan),
|
||||
sections = buildSections(customerPlan.plan),
|
||||
onBackClick = router::pop,
|
||||
onChangePlanClick = { router.push(TangemPayAccountDetailsInnerRoute.SelectPlan) },
|
||||
)
|
||||
|
||||
// TODO v_rodionov: strings hardcoded for now - wait for localization
|
||||
private fun createNotification(customerPlan: TangemPayCustomerTariffPlan): TangemPayCurrentPlanUM.Notification? {
|
||||
val date = customerPlan.formatNextBillingDateOrNull(formatter = DateTimeFormatters.dateMMMd) ?: return null
|
||||
val feeText = customerPlan.plan.formatRecurringFeeOrNull() ?: return null
|
||||
return when (customerPlan.status) {
|
||||
TangemPayCustomerTariffPlan.Status.DOWNGRADE_PENDING -> {
|
||||
val targetPlan = customerPlan.pendingPlan ?: return null
|
||||
TangemPayCurrentPlanUM.Notification(
|
||||
text = stringReference(
|
||||
"Your ${customerPlan.plan.name} plan is active till $date, then we will move you to " +
|
||||
"${targetPlan.name}. $feeText won't be charged.",
|
||||
),
|
||||
button = TangemPayCurrentPlanUM.Notification.Button(
|
||||
text = stringReference("Stay on ${customerPlan.plan.name}"),
|
||||
onClick = {}, // TODO v_rodionov: #[REDACTED_TASK_KEY] - Downgrade
|
||||
),
|
||||
)
|
||||
}
|
||||
TangemPayCustomerTariffPlan.Status.ACTIVE -> TangemPayCurrentPlanUM.Notification(
|
||||
text = stringReference("$feeText monthly fee will be charged on $date"),
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildSections(plan: TangemPayTariffPlan) = persistentListOf(
|
||||
sectionOf(plan, TangemPayTariffPlan.Section.CARD_RELATED, R.string.tangempay_current_plan_section_card),
|
||||
sectionOf(plan, TangemPayTariffPlan.Section.PLAN_RELATED, R.string.tangempay_current_plan_section_plan),
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.tangem.domain.pay.usecase.CreateTariffPlanTransitionOrderUseCase
|
|||
import com.tangem.domain.pay.usecase.GetTangemPayTariffPlanTransitionsUseCase
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.navigation.TangemPayAccountDetailsInnerRoute
|
||||
import com.tangem.features.tangempay.tiers.formatRecurringFeeOrNull
|
||||
import com.tangem.features.tangempay.utils.TangemPayMessagesFactory
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -38,6 +39,10 @@ internal class TangemPaySelectPlanModel @Inject constructor(
|
|||
private val params = paramsContainer.require<TangemPaySelectPlanComponent.Params>()
|
||||
|
||||
private var transitions: List<TangemPayTariffPlanTransition> = emptyList()
|
||||
|
||||
private val allowedTransitions: List<TangemPayTariffPlanTransition>
|
||||
get() = transitions.filter { it.type in ALLOWED_TYPES }
|
||||
|
||||
private var selectedIndex: Int = 0
|
||||
private var isConfirm: Boolean = false
|
||||
private var isProcessing: Boolean = false
|
||||
|
|
@ -52,7 +57,7 @@ internal class TangemPaySelectPlanModel @Inject constructor(
|
|||
private fun loadTransitions() {
|
||||
modelScope.launch {
|
||||
getTransitions(params.userWalletId).onRight { result ->
|
||||
transitions = result.filter { it.type in ALLOWED_TYPES }
|
||||
transitions = result
|
||||
state.update { buildState() }
|
||||
}
|
||||
}
|
||||
|
|
@ -65,13 +70,13 @@ internal class TangemPaySelectPlanModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onSelectClick() {
|
||||
if (transitions.isEmpty()) return
|
||||
if (allowedTransitions.isEmpty()) return
|
||||
isConfirm = true
|
||||
state.update { buildState() }
|
||||
}
|
||||
|
||||
private fun onComparePlansClick() {
|
||||
if (transitions.isEmpty()) return
|
||||
if (allowedTransitions.isEmpty()) return
|
||||
state.update { buildState(showPlanCompare = true) }
|
||||
}
|
||||
|
||||
|
|
@ -90,8 +95,11 @@ internal class TangemPaySelectPlanModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onConfirmClick() {
|
||||
val transition = transitions.getOrNull(selectedIndex) ?: return
|
||||
val transition = allowedTransitions.getOrNull(selectedIndex) ?: return
|
||||
|
||||
// TODO v_rodionov: #[REDACTED_TASK_KEY] - Downgrade
|
||||
if (transition.type != TangemPayTariffPlanTransition.Type.UPGRADE) return
|
||||
|
||||
if (isProcessing) return
|
||||
|
||||
isProcessing = true
|
||||
|
|
@ -118,7 +126,7 @@ internal class TangemPaySelectPlanModel @Inject constructor(
|
|||
} else {
|
||||
resourceReference(R.string.tangempay_select_plan_title)
|
||||
},
|
||||
plans = transitions.map { it.plan.toPlanUM() }.toImmutableList(),
|
||||
plans = allowedTransitions.map { it.plan.toPlanUM() }.toImmutableList(),
|
||||
selectedIndex = selectedIndex,
|
||||
onPlanSelected = ::onPlanSelected,
|
||||
onBackClick = ::onBackClick,
|
||||
|
|
@ -135,14 +143,16 @@ internal class TangemPaySelectPlanModel @Inject constructor(
|
|||
private fun buildCompare(): TangemPaySelectPlanUM.ComparePlans {
|
||||
val plans = transitions.map { it.plan }
|
||||
val orderedTitles = plans
|
||||
.flatMap { it.descriptionItems }
|
||||
.flatMap { plan -> plan.descriptionItems.filter { it.section in COMPARE_SECTIONS } }
|
||||
.sortedWith(compareBy({ it.section.ordinal }, { it.order }))
|
||||
.map { it.title }
|
||||
.distinct()
|
||||
return TangemPaySelectPlanUM.ComparePlans(
|
||||
attributes = orderedTitles.map(::stringReference).toImmutableList(),
|
||||
plans = plans.map { plan ->
|
||||
val valueByTitle = plan.descriptionItems.associate { it.title to it.body }
|
||||
val valueByTitle = plan.descriptionItems
|
||||
.filter { it.section in COMPARE_SECTIONS }
|
||||
.associate { it.title to it.body }
|
||||
TangemPaySelectPlanUM.ComparePlans.Plan(
|
||||
name = stringReference(plan.name),
|
||||
values = orderedTitles
|
||||
|
|
@ -154,8 +164,8 @@ internal class TangemPaySelectPlanModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun buildConfirmContent(): TangemPaySelectPlanUM.Content.Confirm {
|
||||
val transition = transitions[selectedIndex]
|
||||
private fun buildConfirmContent(): TangemPaySelectPlanUM.Content {
|
||||
val transition = allowedTransitions.getOrNull(selectedIndex) ?: return buildSelectContent()
|
||||
val planName = transition.plan.name
|
||||
val isUpgrade = transition.type == TangemPayTariffPlanTransition.Type.UPGRADE
|
||||
return TangemPaySelectPlanUM.Content.Confirm(
|
||||
|
|
@ -181,9 +191,7 @@ internal class TangemPaySelectPlanModel @Inject constructor(
|
|||
planName: String,
|
||||
isUpgrade: Boolean,
|
||||
): ImmutableList<TangemPaySelectPlanUM.PointUM> = if (isUpgrade) {
|
||||
val feeText = transition.plan.descriptionItems
|
||||
.firstOrNull { it.section == TangemPayTariffPlan.Section.PLAN_RELATED }
|
||||
?.title
|
||||
val feeText = transition.plan.formatRecurringFeeOrNull()
|
||||
listOf(
|
||||
stringReference("You will get your virtual Visa $planName in minutes"),
|
||||
if (feeText != null) {
|
||||
|
|
@ -205,7 +213,8 @@ internal class TangemPaySelectPlanModel @Inject constructor(
|
|||
name = stringReference(name),
|
||||
imageUrl = images.firstOrNull { it.type == TangemPayTariffPlan.Image.Type.MAIN }?.url,
|
||||
points = descriptionItems
|
||||
.sortedWith(compareBy({ it.section.ordinal }, { it.order }))
|
||||
.filter { it.section == TangemPayTariffPlan.Section.ONBOARDING_RELATED }
|
||||
.sortedBy { it.order }
|
||||
.map { item ->
|
||||
TangemPaySelectPlanUM.PointUM(
|
||||
title = stringReference(item.title),
|
||||
|
|
@ -220,5 +229,9 @@ internal class TangemPaySelectPlanModel @Inject constructor(
|
|||
TangemPayTariffPlanTransition.Type.UPGRADE,
|
||||
TangemPayTariffPlanTransition.Type.DOWNGRADE,
|
||||
)
|
||||
private val COMPARE_SECTIONS = setOf(
|
||||
TangemPayTariffPlan.Section.CARD_RELATED,
|
||||
TangemPayTariffPlan.Section.PLAN_RELATED,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -507,6 +507,7 @@ internal class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<T
|
|||
onAddCardClick = {},
|
||||
isAddCardEnabled = true,
|
||||
),
|
||||
isNegative = false,
|
||||
),
|
||||
isBalanceHidden = false,
|
||||
addToWalletBlockState = AddToWalletBlockState(
|
||||
|
|
|
|||
|
|
@ -255,6 +255,7 @@ private fun ErrorMessage(config: NotificationConfig, modifier: Modifier = Modifi
|
|||
buttonUM = TangemButtonUM(
|
||||
text = button.text,
|
||||
onClick = button.onClick,
|
||||
isLoading = button.shouldShowProgress,
|
||||
iconPosition = TangemButtonIconPosition.End,
|
||||
tangemIconUM = if (iconResId != null) {
|
||||
TangemIconUM.Icon(
|
||||
|
|
@ -352,10 +353,10 @@ private fun BalanceBlock(
|
|||
style = TangemTheme.typography3.heading.medium,
|
||||
)
|
||||
is TangemPayDetailsBalanceBlockState.Content -> {
|
||||
val balanceColor = if (animatedState.isMuted) {
|
||||
TangemTheme.colors3.text.secondary
|
||||
} else {
|
||||
TangemTheme.colors3.text.primary
|
||||
val balanceColor = when {
|
||||
animatedState.isMuted -> TangemTheme.colors3.text.secondary
|
||||
animatedState.isNegative -> TangemTheme.colors3.text.status.error
|
||||
else -> TangemTheme.colors3.text.primary
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE),
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ internal interface TangemPayDetailIntents {
|
|||
fun onClickAddFunds()
|
||||
fun onClickWithdraw()
|
||||
fun onClickTermsAndLimits()
|
||||
fun onClickVisaBenefits()
|
||||
fun onClickCurrentPlan(tariffPlan: TangemPayCustomerTariffPlan)
|
||||
fun onCancelPlusTransition(orderId: String)
|
||||
fun onCardClick(cardId: String)
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ internal class TangemPayActionButtonsTransformerTest {
|
|||
cardsBlockState = null,
|
||||
fiatBalance = TextReference.EMPTY,
|
||||
isBalanceFlickering = false,
|
||||
isNegative = false,
|
||||
),
|
||||
addToWalletBlockState = null,
|
||||
isBalanceHidden = false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue