Updated on 2026-08-14
This commit is contained in:
parent
65d2e84c6e
commit
9e986a6cb3
21 changed files with 180 additions and 77 deletions
|
|
@ -20,6 +20,7 @@ import com.tangem.domain.models.pay.TangemPayCardState
|
|||
import com.tangem.domain.models.pay.isFrozen
|
||||
import com.tangem.domain.models.pay.thumbnailUrl
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.model.transformers.DetailsBalanceTransformer
|
||||
import com.tangem.features.tangempay.utils.TangemPayDetailIntents
|
||||
import com.tangem.features.tangempay.utils.hasWithdrawableAmount
|
||||
import com.tangem.features.tangempay.utils.isFresh
|
||||
|
|
@ -28,7 +29,7 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
import kotlinx.collections.immutable.toImmutableList
|
||||
import com.tangem.core.ui.R as CoreUiR
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
internal class TangemPayDetailsStateFactory(
|
||||
private val onBack: () -> Unit,
|
||||
private val onOpenMenu: () -> Unit,
|
||||
|
|
@ -159,6 +160,51 @@ internal class TangemPayDetailsStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun getInactiveState(status: PaymentAccountStatusValue.Inactive): TangemPayDetailsUM {
|
||||
val notification = notificationFactory.createAwaitingDepositConfig(status.tariffPlan)
|
||||
return TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(
|
||||
onBackClick = onBack,
|
||||
onOpenMenu = onOpenMenu,
|
||||
items = getTopBarMenuItems(),
|
||||
itemsV2 = getTopBarMenuItemsV2(tariffPlan = null),
|
||||
),
|
||||
pullToRefreshConfig = PullToRefreshConfig(
|
||||
isRefreshing = false,
|
||||
onRefresh = intents::onRefreshSwipe,
|
||||
),
|
||||
balanceBlockState = TangemPayDetailsBalanceBlockState.Content(
|
||||
actionButtons = getActionButtonsConfig(
|
||||
isAddFundsEnabled = true,
|
||||
isWithdrawEnabled = false,
|
||||
),
|
||||
cardsBlockState = TangemPayDetailsBalanceBlockState.CardsBlockState(
|
||||
cards = persistentListOf(
|
||||
TangemPayDetailsBalanceBlockState.Card(
|
||||
lastDigits = "",
|
||||
imageUrl = null,
|
||||
onClick = {},
|
||||
isEnabled = false,
|
||||
isFrozen = false,
|
||||
state = TangemPayCardUiState.InProgress,
|
||||
),
|
||||
),
|
||||
onAddCardClick = intents::onAddCardClick,
|
||||
isAddCardEnabled = false,
|
||||
),
|
||||
fiatBalance = DetailsBalanceTransformer.getFiatBalanceText(status.fiatBalance),
|
||||
isInactive = true,
|
||||
isNegative = false,
|
||||
isBalanceFlickering = false,
|
||||
),
|
||||
isBalanceHidden = false,
|
||||
addToWalletBlockState = null,
|
||||
errorNotificationConfig = notification,
|
||||
accountDeactivatedNotificationConfig = null,
|
||||
cashbackBlockState = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getTopBarMenuItems(): ImmutableList<TangemDropdownMenuItem> {
|
||||
return persistentListOf(
|
||||
TangemDropdownMenuItem(
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ internal sealed class TangemPayDetailsBalanceBlockState {
|
|||
val fiatBalance: TextReference,
|
||||
val isBalanceFlickering: Boolean,
|
||||
val isNegative: Boolean,
|
||||
val isInactive: Boolean,
|
||||
val isMuted: Boolean = false,
|
||||
) : TangemPayDetailsBalanceBlockState()
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import com.tangem.features.tangempay.entity.TangemPayDetailsStateFactory
|
|||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||
import com.tangem.features.tangempay.model.transformers.*
|
||||
import com.tangem.features.tangempay.navigation.TangemPayAccountDetailsInnerRoute
|
||||
import com.tangem.features.tangempay.tiers.feeCurrency
|
||||
import com.tangem.features.tangempay.utils.*
|
||||
import com.tangem.features.tokendetails.ExpressTransactionsEvent
|
||||
import com.tangem.features.tokendetails.ExpressTransactionsEventListener
|
||||
|
|
@ -171,6 +172,9 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
)
|
||||
uiState.update { balanceTransformer.transform(stateFactory.getLoadedState(state)) }
|
||||
}
|
||||
is PaymentAccountStatusValue.Inactive -> uiState.update {
|
||||
stateFactory.getInactiveState(state)
|
||||
}
|
||||
else -> uiState.update { stateFactory.getLoadingState() }
|
||||
}
|
||||
}
|
||||
|
|
@ -484,11 +488,6 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
)
|
||||
return
|
||||
}
|
||||
val activeCardsCount = currentStatus.value.ifLoadedOrNull { loaded -> loaded.cards.count() } ?: 0
|
||||
if (activeCardsCount >= ALLOWED_MAX_CARDS_COUNT) {
|
||||
uiMessageSender.send(TangemPayMessagesFactory.createMaximumCardsIssued(maxCards = ALLOWED_MAX_CARDS_COUNT))
|
||||
return
|
||||
}
|
||||
modelScope.launch {
|
||||
val offer = getCustomerOffers.additionalCardOffer(userWalletId).getOrNull()
|
||||
if (offer == null) {
|
||||
|
|
@ -569,8 +568,4 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
private fun showBottomSheetError(type: TangemPayDetailsErrorType) {
|
||||
uiMessageSender.send(message = TangemPayMessagesFactory.createErrorMessage(errorType = type))
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val ALLOWED_MAX_CARDS_COUNT = 3
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import java.util.Currency
|
|||
|
||||
internal class DetailsBalanceTransformer(
|
||||
private val fiatBalance: PaymentAccountStatusValue.FiatBalance,
|
||||
private val isInactive: Boolean = false,
|
||||
private val isMuted: Boolean = false,
|
||||
) : Transformer<TangemPayDetailsUM> {
|
||||
|
||||
|
|
@ -23,18 +24,21 @@ internal class DetailsBalanceTransformer(
|
|||
cardsBlockState = prevState.balanceBlockState.cardsBlockState,
|
||||
isMuted = isMuted,
|
||||
isNegative = fiatBalance.availableBalance.signum() < 0,
|
||||
isInactive = isInactive,
|
||||
)
|
||||
return prevState.copy(balanceBlockState = balance)
|
||||
}
|
||||
|
||||
private fun getFiatBalanceText(fiatBalance: PaymentAccountStatusValue.FiatBalance): TextReference {
|
||||
val currency = Currency.getInstance(fiatBalance.currency)
|
||||
return fiatBalance.availableBalance.formatStyled {
|
||||
fiat(
|
||||
fiatCurrencyCode = currency.currencyCode,
|
||||
fiatCurrencySymbol = currency.symbol,
|
||||
spanStyleReference = { TangemTheme.typography3.heading.medium.toSpanStyle() },
|
||||
)
|
||||
companion object {
|
||||
fun getFiatBalanceText(fiatBalance: PaymentAccountStatusValue.FiatBalance): TextReference {
|
||||
val currency = Currency.getInstance(fiatBalance.currency)
|
||||
return fiatBalance.availableBalance.formatStyled {
|
||||
fiat(
|
||||
fiatCurrencyCode = currency.currencyCode,
|
||||
fiatCurrencySymbol = currency.symbol,
|
||||
spanStyleReference = { TangemTheme.typography3.heading.medium.toSpanStyle() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,9 @@ import com.tangem.domain.models.account.TangemPayCustomerTariffPlan
|
|||
import com.tangem.domain.models.account.TangemPayTariffPlan
|
||||
import org.joda.time.format.DateTimeFormatter
|
||||
|
||||
val TangemPayTariffPlan.feeCurrency: String?
|
||||
get() = fees.firstOrNull()?.currency
|
||||
|
||||
fun TangemPayTariffPlan.formatRecurringFeeOrNull(): String? {
|
||||
val fee = fees.find { it.type == TangemPayTariffPlan.Fee.Type.RECURRING } ?: return null
|
||||
val currency = getJavaCurrencyByCode(fee.currency)
|
||||
|
|
|
|||
|
|
@ -510,6 +510,7 @@ internal class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<T
|
|||
isAddCardEnabled = true,
|
||||
),
|
||||
isNegative = false,
|
||||
isInactive = false,
|
||||
),
|
||||
isBalanceHidden = false,
|
||||
addToWalletBlockState = AddToWalletBlockState(
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import com.tangem.core.ui.components.topFade
|
|||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.ds.button.*
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds2.badge.TangemBadge
|
||||
import com.tangem.core.ui.ds.message.TangemMessage
|
||||
import com.tangem.core.ui.ds.message.TangemMessageEffect
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
|
|
@ -359,39 +360,7 @@ private fun BalanceBlock(
|
|||
fadeOut(animationSpec = tween(durationMillis = 90))
|
||||
},
|
||||
) { animatedState ->
|
||||
when (animatedState) {
|
||||
is TangemPayDetailsBalanceBlockState.Loading -> TangemShimmer(
|
||||
style = TangemTheme.typography3.heading.medium,
|
||||
)
|
||||
is TangemPayDetailsBalanceBlockState.Content -> {
|
||||
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),
|
||||
text = animatedState.fiatBalance.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography3.display.medium.applyBladeBrush(
|
||||
isEnabled = animatedState.isBalanceFlickering,
|
||||
textColor = balanceColor,
|
||||
),
|
||||
color = balanceColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
autoSize = TextAutoSize.StepBased(
|
||||
minFontSize = TangemTheme.typography3.heading.medium.fontSize,
|
||||
maxFontSize = TangemTheme.typography3.display.medium.fontSize,
|
||||
),
|
||||
)
|
||||
}
|
||||
is TangemPayDetailsBalanceBlockState.Error -> Text(
|
||||
modifier = Modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE),
|
||||
text = DASH_SIGN.orMaskWithStars(isBalanceHidden),
|
||||
style = TangemTheme.typography3.display.medium,
|
||||
color = TangemTheme.colors3.text.primary,
|
||||
)
|
||||
}
|
||||
BalanceValue(animatedState, isBalanceHidden)
|
||||
}
|
||||
|
||||
Text(
|
||||
|
|
@ -400,6 +369,58 @@ private fun BalanceBlock(
|
|||
color = TangemTheme.colors3.text.secondary,
|
||||
style = TangemTheme.typography3.caption.medium,
|
||||
)
|
||||
|
||||
when (state) {
|
||||
is TangemPayDetailsBalanceBlockState.Loading -> Unit
|
||||
is TangemPayDetailsBalanceBlockState.Content -> {
|
||||
TangemBadge(
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens2.x1),
|
||||
text = resourceReference(R.string.tangempay_status_inactive),
|
||||
variant = TangemBadge.Variant.Outline,
|
||||
status = TangemBadge.Status.Warning,
|
||||
size = TangemBadge.Size.X6,
|
||||
iconStart = TangemIconUM.Icon(iconRes = CoreUiR.drawable.ic_information_24),
|
||||
)
|
||||
}
|
||||
is TangemPayDetailsBalanceBlockState.Error -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BalanceValue(state: TangemPayDetailsBalanceBlockState, isBalanceHidden: Boolean) {
|
||||
when (state) {
|
||||
is TangemPayDetailsBalanceBlockState.Loading -> TangemShimmer(
|
||||
style = TangemTheme.typography3.heading.medium,
|
||||
)
|
||||
is TangemPayDetailsBalanceBlockState.Content -> {
|
||||
val balanceColor = when {
|
||||
state.isMuted -> TangemTheme.colors3.text.secondary
|
||||
state.isNegative -> TangemTheme.colors3.text.status.error
|
||||
else -> TangemTheme.colors3.text.primary
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE),
|
||||
text = state.fiatBalance.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography3.display.medium.applyBladeBrush(
|
||||
isEnabled = state.isBalanceFlickering,
|
||||
textColor = balanceColor,
|
||||
),
|
||||
color = balanceColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
autoSize = TextAutoSize.StepBased(
|
||||
minFontSize = TangemTheme.typography3.heading.medium.fontSize,
|
||||
maxFontSize = TangemTheme.typography3.display.medium.fontSize,
|
||||
),
|
||||
)
|
||||
}
|
||||
is TangemPayDetailsBalanceBlockState.Error -> Text(
|
||||
modifier = Modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE),
|
||||
text = DASH_SIGN.orMaskWithStars(isBalanceHidden),
|
||||
style = TangemTheme.typography3.display.medium,
|
||||
color = TangemTheme.colors3.text.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ internal val AccountStatus.Payment.cryptoCurrency: CryptoCurrency.Token
|
|||
get() = when (val v = value) {
|
||||
is PaymentAccountStatusValue.Loaded -> v.cryptoCurrency
|
||||
is PaymentAccountStatusValue.Deactivated -> v.cryptoCurrency
|
||||
is PaymentAccountStatusValue.Inactive -> v.cryptoCurrency
|
||||
is PaymentAccountStatusValue.AwaitingPlanSelection -> v.cryptoCurrency
|
||||
else -> error("TangemPayDetails opened with unsupported status: $v")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,10 @@ import com.tangem.core.ui.components.bottomsheets.message.*
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.message.BottomSheetMessage
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.core.ui.res.generated.icons.Icons
|
||||
import com.tangem.core.ui.res.generated.icons.ic_error_28
|
||||
import com.tangem.core.ui.res.generated.icons.ic_snowflake_20
|
||||
import com.tangem.core.ui.res.generated.icons.ic_sun_20
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
|
||||
|
|
@ -151,26 +149,6 @@ internal object TangemPayMessagesFactory {
|
|||
}
|
||||
}
|
||||
|
||||
fun createMaximumCardsIssued(maxCards: Int): BottomSheetMessage {
|
||||
return bottomSheetMessage {
|
||||
infoBlock {
|
||||
vector(Icons.ic_error_28) {
|
||||
type = MessageBottomSheetUM.Vector.Type.Attention
|
||||
backgroundType = MessageBottomSheetUM.Vector.BackgroundType.Attention
|
||||
}
|
||||
title = resourceReference(R.string.tangempay_maximum_cards_issued_title)
|
||||
body = resourceReference(
|
||||
id = R.string.tangempay_maximum_cards_issued_description,
|
||||
formatArgs = wrappedList(maxCards),
|
||||
)
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.common_got_it)
|
||||
onClick { closeBs() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO v_rodionov: #[REDACTED_TASK_KEY] fix hardcoded strings
|
||||
fun createStayOnPlanMessage(
|
||||
planName: String,
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ internal class CashbackBlockTransformerTest {
|
|||
fiatBalance = TextReference.EMPTY,
|
||||
isBalanceFlickering = false,
|
||||
isNegative = false,
|
||||
isInactive = false,
|
||||
),
|
||||
addToWalletBlockState = null,
|
||||
isBalanceHidden = false,
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ internal class TangemPayActionButtonsTransformerTest {
|
|||
fiatBalance = TextReference.EMPTY,
|
||||
isBalanceFlickering = false,
|
||||
isNegative = false,
|
||||
isInactive = false,
|
||||
),
|
||||
addToWalletBlockState = null,
|
||||
isBalanceHidden = false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue