Updated on 2026-08-14

This commit is contained in:
Tangem 2026-01-29 14:49:00 +04:00
parent 1b469c093c
commit f87629742b
8 changed files with 128 additions and 33 deletions

View file

@ -1469,6 +1469,9 @@
<string name="tangem_pay_freeze_card_freeze">Freeze</string>
<string name="tangem_pay_freeze_card_success">Your card is frozen.</string>
<string name="tangem_pay_get_help">Get Help</string>
<string name="tangem_pay_history_item_spend_mc_declined_reason">Reason: %s</string>
<string name="tangem_pay_history_item_spend_mc_title_format" formatted="false">%s · %s</string>
<string name="tangem_pay_history_item_spend_mcc">MCC %s</string>
<string name="tangem_pay_other">Other</string>
<string name="tangem_pay_rooted_device_subtitle">Unable to use on rooted devices</string>
<string name="tangem_pay_status_completed">Completed</string>

View file

@ -46,6 +46,7 @@ internal class TangemPayTxHistoryItemConverter(moshi: Moshi) :
merchantCategory = spend.merchantCategory,
status = TangemPayTxHistoryItemStatusConverter.convert(spend.status),
enrichedMerchantIconUrl = spend.enrichedMerchantIcon,
declinedReason = spend.declinedReason,
)
}

View file

@ -29,6 +29,7 @@ sealed class TangemPayTxHistoryItem {
val merchantCategory: String?,
val status: Status,
val enrichedMerchantIconUrl: String?,
val declinedReason: String?,
) : TangemPayTxHistoryItem()
@Serializable

View file

@ -17,10 +17,17 @@ internal data class TangemPayTxHistoryDetailsUM(
val transactionAmountColor: ColorReference,
val localTransactionText: String?,
val labelState: LabelUM?,
val notification: NotificationConfig?,
val notification: NotificationState?,
val buttons: ImmutableList<ButtonState>,
val dismiss: () -> Unit,
) {
data class NotificationState(
val config: NotificationConfig,
val titleColor: ColorReference,
val iconTint: ColorReference,
val containerColor: ColorReference?,
)
data class ButtonState(val text: TextReference, val onClick: () -> Unit, val startIcon: ImageReference.Res? = null)
}

View file

@ -0,0 +1,46 @@
package com.tangem.features.tangempay.model.transformers
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.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.features.tangempay.details.impl.R
import com.tangem.utils.converter.Converter
internal object PayDetailsSpendSubtitleConverter : Converter<TangemPayTxHistoryItem.Spend, TextReference> {
override fun convert(value: TangemPayTxHistoryItem.Spend): TextReference {
val merchantCategory = value.merchantCategory
val enrichedMerchantCategory = value.enrichedMerchantCategory
val merchantCategoryCode = value.merchantCategoryCode
val categoryCodeText = if (!merchantCategoryCode.isNullOrEmpty()) {
resourceReference(
id = R.string.tangem_pay_history_item_spend_mcc,
formatArgs = wrappedList(merchantCategoryCode),
)
} else {
null
}
val categoryText = when {
// if merchantCategory isNotEmpty use this
!merchantCategory.isNullOrEmpty() -> stringReference(merchantCategory)
// If merchantCategory empty or null but enrichedMerchantCategory is not empty
!enrichedMerchantCategory.isNullOrEmpty() -> stringReference(enrichedMerchantCategory)
else -> resourceReference(R.string.tangem_pay_other)
}
return if (categoryCodeText != null) {
resourceReference(
id = R.string.tangem_pay_history_item_spend_mc_title_format,
formatArgs = wrappedList(categoryText, categoryCodeText),
)
} else {
categoryText
}
}
}

View file

@ -7,6 +7,7 @@ import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.price
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
@ -24,7 +25,7 @@ internal object TangemPayTxHistoryDetailsConverter :
Converter<TangemPayTxHistoryDetailsConverter.Input, TangemPayTxHistoryDetailsUM> {
private val dateFormatter = DateTimeFormatters.getBestFormatterBySkeleton("dd MMMM")
private val paySpendSubtitleConverter = PaySpendSubtitleConverter
private val paySpendSubtitleConverter = PayDetailsSpendSubtitleConverter
override fun convert(value: Input): TangemPayTxHistoryDetailsUM {
val transaction = value.item
@ -140,13 +141,16 @@ internal object TangemPayTxHistoryDetailsConverter :
private fun TangemPayTxHistoryItem.extractAmountColor(): ColorReference {
return when (this) {
is TangemPayTxHistoryItem.Fee,
is TangemPayTxHistoryItem.Spend,
is TangemPayTxHistoryItem.Payment,
-> themedColor { TangemTheme.colors.text.primary1 }
is TangemPayTxHistoryItem.Collateral -> when (this.type) {
TangemPayTxHistoryItem.Type.Deposit -> themedColor { TangemTheme.colors.text.accent }
TangemPayTxHistoryItem.Type.Withdrawal -> themedColor { TangemTheme.colors.text.primary1 }
}
is TangemPayTxHistoryItem.Spend -> when (this.status) {
TangemPayTxHistoryItem.Status.DECLINED -> themedColor { TangemTheme.colors.text.warning }
else -> themedColor { TangemTheme.colors.text.primary1 }
}
}
}
@ -200,21 +204,39 @@ internal object TangemPayTxHistoryDetailsConverter :
}
}
private fun TangemPayTxHistoryItem.extractNotification(): NotificationConfig? {
private fun TangemPayTxHistoryItem.extractNotification(): TangemPayTxHistoryDetailsUM.NotificationState? {
return when (this) {
is TangemPayTxHistoryItem.Payment -> null
is TangemPayTxHistoryItem.Collateral -> null
is TangemPayTxHistoryItem.Fee -> NotificationConfig(
title = resourceReference(R.string.tangem_pay_transaction_fee_notification_text),
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
)
is TangemPayTxHistoryItem.Spend -> when (this.status) {
TangemPayTxHistoryItem.Status.DECLINED -> NotificationConfig(
title = resourceReference(R.string.tangem_pay_transaction_declined_notification_text),
is TangemPayTxHistoryItem.Fee -> TangemPayTxHistoryDetailsUM.NotificationState(
config = NotificationConfig(
title = resourceReference(R.string.tangem_pay_transaction_fee_notification_text),
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
)
),
titleColor = themedColor { TangemTheme.colors.text.tertiary },
iconTint = themedColor { TangemTheme.colors.icon.secondary },
containerColor = null,
)
is TangemPayTxHistoryItem.Spend -> when (this.status) {
TangemPayTxHistoryItem.Status.DECLINED ->
TangemPayTxHistoryDetailsUM.NotificationState(
config = NotificationConfig(
title = if (declinedReason.isNullOrEmpty()) {
resourceReference(R.string.tangem_pay_transaction_declined_notification_text)
} else {
resourceReference(
id = R.string.tangem_pay_history_item_spend_mc_declined_reason,
formatArgs = wrappedList(requireNotNull(declinedReason)),
)
},
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
),
titleColor = themedColor { TangemTheme.colors.text.warning },
iconTint = themedColor { TangemTheme.colors.icon.warning },
containerColor = themedColor { TangemColorPalette.Amaranth.copy(alpha = 0.1F) },
)
TangemPayTxHistoryItem.Status.PENDING,
TangemPayTxHistoryItem.Status.COMPLETED,
TangemPayTxHistoryItem.Status.RESERVED,

View file

@ -37,10 +37,10 @@ internal class TangemPayTxHistoryItemsConverter(
val localDate = spend.date.withZone(DateTimeZone.getDefault())
val amountPrefix = when {
spend.amount.isZero() -> ""
spend.status == TangemPayTxHistoryItem.Status.DECLINED -> ""
else -> StringsSigns.MINUS
spend.status == TangemPayTxHistoryItem.Status.DECLINED || spend.amount.isPositive() -> StringsSigns.MINUS
else -> StringsSigns.PLUS
}
val amount = amountPrefix + spend.amount.format {
val amount = amountPrefix + spend.amount.abs().format {
fiat(fiatCurrencyCode = spend.currency.currencyCode, fiatCurrencySymbol = spend.currency.symbol)
}
return TangemPayTransactionState.Content.Spend(
@ -48,8 +48,9 @@ internal class TangemPayTxHistoryItemsConverter(
onClick = { txHistoryUiActions.onTransactionClick(spend) },
amount = amount,
amountColor = themedColor {
when (spend.status) {
TangemPayTxHistoryItem.Status.DECLINED -> TangemTheme.colors.text.warning
when {
spend.status == TangemPayTxHistoryItem.Status.DECLINED -> TangemTheme.colors.text.warning
amountPrefix == StringsSigns.PLUS -> TangemTheme.colors.text.accent
else -> TangemTheme.colors.text.primary1
}
},

View file

@ -26,6 +26,7 @@ import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.tangempay.details.impl.R
@ -83,21 +84,24 @@ internal fun TangemPayTxHistoryDetailsContent(state: TangemPayTxHistoryDetailsUM
style = TangemTheme.typography.head,
color = state.transactionAmountColor.resolveReference(),
)
state.localTransactionText?.let { localTransaction ->
if (state.localTransactionText != null) {
Text(
modifier = Modifier.padding(top = 4.dp),
text = localTransaction.orMaskWithStars(state.isBalanceHidden),
text = state.localTransactionText.orMaskWithStars(state.isBalanceHidden),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
}
state.labelState?.let { Label(state = state.labelState, modifier = Modifier.padding(top = 12.dp)) }
if (state.labelState != null) {
Label(state = state.labelState, modifier = Modifier.padding(top = 12.dp))
}
SpacerH32()
state.notification?.let {
if (state.notification != null) {
Notification(
config = state.notification,
titleColor = TangemTheme.colors.text.tertiary,
iconTint = TangemTheme.colors.icon.secondary,
config = state.notification.config,
titleColor = state.notification.titleColor.resolveReference(),
iconTint = state.notification.iconTint.resolveReference(),
containerColor = state.notification.containerColor?.resolveReference(),
)
}
ButtonsContainer(
@ -195,10 +199,15 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr
text = resourceReference(R.string.tangem_pay_status_declined),
style = LabelStyle.WARNING,
),
notification = NotificationConfig(
title = stringReference("The bank rejected this transaction request."),
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
notification = TangemPayTxHistoryDetailsUM.NotificationState(
config = NotificationConfig(
title = stringReference("The bank rejected this transaction request."),
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
),
titleColor = themedColor { TangemTheme.colors.text.warning },
iconTint = themedColor { TangemTheme.colors.icon.warning },
containerColor = themedColor { TangemColorPalette.Amaranth.copy(alpha = 0.1F) },
),
buttons = persistentListOf(
TangemPayTxHistoryDetailsUM.ButtonState(
@ -240,10 +249,15 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr
transactionAmountColor = themedColor { TangemTheme.colors.text.primary1 },
localTransactionText = null,
labelState = null,
notification = NotificationConfig(
title = stringReference("This fee goes to cover the cost of handling your transfer."),
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
notification = TangemPayTxHistoryDetailsUM.NotificationState(
config = NotificationConfig(
title = stringReference("This fee goes to cover the cost of handling your transfer."),
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
),
titleColor = themedColor { TangemTheme.colors.text.tertiary },
iconTint = themedColor { TangemTheme.colors.icon.secondary },
containerColor = null,
),
buttons = persistentListOf(
TangemPayTxHistoryDetailsUM.ButtonState(