From cff379462f8cea3dadda0c8610cdfa2dc0f7dca7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 1 Jun 2026 16:32:51 +0200 Subject: [PATCH] Updated on 2026-08-14 --- core/res/src/main/res/values/strings.xml | 29 +- .../core/ui/ds2/surface/TangemSurface.kt | 14 +- .../src/main/res/drawable/ic_replace_20.xml | 27 -- .../DefaultTangemPayFeatureToggles.kt | 3 +- .../TangemPayTxHistoryDetailsComponent.kt | 14 +- .../entity/TangemPayTxHistoryDetailsUM.kt | 56 ++- .../tangempay/model/TangemPayCardPageModel.kt | 4 +- .../model/TangemPayTxHistoryDetailsModel.kt | 41 +- .../TangemPayTxHistoryDetailsConverter.kt | 2 +- .../TangemPayTxHistoryDetailsConverterV2.kt | 322 +++++++++++++++ .../tangempay/ui/TangempayTxDetailsUi.kt | 18 +- .../tangempay/ui/TangempayTxDetailsUiV2.kt | 389 ++++++++++++++++++ 12 files changed, 848 insertions(+), 71 deletions(-) delete mode 100644 core/ui/src/main/res/drawable/ic_replace_20.xml create mode 100644 features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayTxHistoryDetailsConverterV2.kt create mode 100644 features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangempayTxDetailsUiV2.kt diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 7806491a95..18fff34c69 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1677,7 +1677,7 @@ Unable to rename card Card frozen Card payment - It will disappear from payment account + It will disappear from the app Close card Go back Close your card? @@ -1698,6 +1698,7 @@ MCC %s Other PIN-code + Purchase Unable to use on rooted devices Completed Declined @@ -1706,6 +1707,8 @@ Terms, Fees & Limits Terms and fees The bank rejected this transaction request. + Category + MCC A fee is charged in accordance with the service tariffs The transaction was partially or fully reversed by the merchant Keep using your money. You can freeze anytime. @@ -1824,6 +1827,24 @@ your profile. You can have up to 3 cards. Delete one to add a new card. Maximum Cards Issued + Yes – to operate a regulated Visa card, identity verification is mandatory. KYC is handled by Sumsub (compliance partner). + Do I have to share my docs? + No. KYC applies only to the Tangem Pay account. Your Tangem Wallet itself remains a separate, self-custodial, non-KYC environment. + Does the KYC associate with my wallet? + Sumsub – a globally regulated KYC provider, trusted by 4,000+ financial institutions – verifies your identity and securely stores the results under ISO 27001 and SOC 2 standards. + Who stores my personal data and how is it protected? + Card balance nominated in USDC on Polygon, but you can use any asset (USDT, SOL, ETH, BTC, XRP etc.) to fund it using Tangem\'s convenient built-in swap mechanisms. + What crypto can I spend? + Spend crypto anywhere — no banks, no middlemen, no exchanges. The power of self-custody meets everyday payments. + Buy online and via Apple Pay + Accepted worldwide + Use anywhere in the world + FX fee is just 1% + Get your Tangem Pay Card + Pay what you see + No purchase fees, \n1 USDC = 1 USD + No surprises + $0 monthly fee\n$0 topup fee Get your free Tangem Visa virtual card Use USDC for everyday payments Get card @@ -1852,16 +1873,16 @@ Replace your card? We’re fixing a technical issue. Please try again later. Service temporarily unavailable - Unable to display details. However, card payments are still working. + The service is currently unreachable. Please try again later. Set \nPIN code - Card deactivated + Account closed Replacing your card Use your card or ring to renew session Use your card or ring to renew session Renew session Payment account session expired Use USDC for everyday payments - Tangem Pay is temporarily unreachable + Tangem Pay is temporarily unavailable Tangem Pay Send USDC Polygon to your account’s address From another wallet or exchange diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds2/surface/TangemSurface.kt b/core/ui/src/main/java/com/tangem/core/ui/ds2/surface/TangemSurface.kt index 8a6e16455b..126d853eb8 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds2/surface/TangemSurface.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds2/surface/TangemSurface.kt @@ -1,21 +1,14 @@ package com.tangem.core.ui.ds2.surface -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.LocalIndication -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable +import androidx.compose.foundation.* import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ripple.RippleAlpha import androidx.compose.material3.LocalRippleConfiguration import androidx.compose.material3.RippleConfiguration -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.NonRestartableComposable -import androidx.compose.runtime.ReadOnlyComposable -import androidx.compose.runtime.remember +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Offset @@ -94,6 +87,7 @@ fun TangemSurface( onClick = onClick!!, ) }, + contentAlignment = Alignment.Center, ) { content() } diff --git a/core/ui/src/main/res/drawable/ic_replace_20.xml b/core/ui/src/main/res/drawable/ic_replace_20.xml deleted file mode 100644 index 3fdf7b9e3d..0000000000 --- a/core/ui/src/main/res/drawable/ic_replace_20.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/DefaultTangemPayFeatureToggles.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/DefaultTangemPayFeatureToggles.kt index 3512628da8..0ea33aafcd 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/DefaultTangemPayFeatureToggles.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/DefaultTangemPayFeatureToggles.kt @@ -7,5 +7,6 @@ internal class DefaultTangemPayFeatureToggles( private val featureTogglesManager: FeatureTogglesManager, ) : TangemPayFeatureToggles { override val isRedesignEnabled: Boolean - get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.AND_15368_VISA_PAY_REDESIGN) + get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.AND_15368_VISA_PAY_REDESIGN) && + featureTogglesManager.isFeatureEnabled(FeatureToggles.APP_REDESIGN_ENABLED) } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/txHistory/TangemPayTxHistoryDetailsComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/txHistory/TangemPayTxHistoryDetailsComponent.kt index b0a795d1af..5b6e5d8793 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/txHistory/TangemPayTxHistoryDetailsComponent.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/txHistory/TangemPayTxHistoryDetailsComponent.kt @@ -5,9 +5,12 @@ import androidx.compose.runtime.getValue import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.res.LocalVisaRedesignEnabled import com.tangem.features.tangempay.components.TangemPayTransactionBottomSheetComponent +import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUiState import com.tangem.features.tangempay.model.TangemPayTxHistoryDetailsModel import com.tangem.features.tangempay.ui.TangemPayTxHistoryDetailsContent +import com.tangem.features.tangempay.ui.TangemPayTxHistoryDetailsContentV2 import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -25,8 +28,15 @@ internal class TangemPayTxHistoryDetailsComponent @AssistedInject constructor( @Composable override fun BottomSheet() { - val state by model.uiState.collectAsStateWithLifecycle() - TangemPayTxHistoryDetailsContent(state = state) + val states by model.uiState.collectAsStateWithLifecycle() + when (val uiState = states.toUiState(isRedesignEnabled = LocalVisaRedesignEnabled.current)) { + is TangemPayTxHistoryDetailsUiState.Legacy -> { + TangemPayTxHistoryDetailsContent(state = uiState.state) + } + is TangemPayTxHistoryDetailsUiState.Redesign -> { + TangemPayTxHistoryDetailsContentV2(state = uiState.state) + } + } } @AssistedFactory diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayTxHistoryDetailsUM.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayTxHistoryDetailsUM.kt index b240e9c159..aa732fd535 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayTxHistoryDetailsUM.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayTxHistoryDetailsUM.kt @@ -1,7 +1,9 @@ package com.tangem.features.tangempay.entity +import androidx.compose.runtime.Immutable import com.tangem.core.ui.components.label.entity.LabelUM import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.extensions.ColorReference import com.tangem.core.ui.extensions.ImageReference import com.tangem.core.ui.extensions.TextReference @@ -28,6 +30,58 @@ internal data class TangemPayTxHistoryDetailsUM( val iconTint: ColorReference, val containerColor: ColorReference?, ) +} - data class ButtonState(val text: TextReference, val onClick: () -> Unit, val startIcon: ImageReference.Res? = null) +internal data class ButtonState( + val text: TextReference, + val onClick: () -> Unit, + val startIcon: ImageReference.Res? = null, +) + +internal data class TangemPayTxHistoryDetailsUMV2( + val isBalanceHidden: Boolean, + val title: TextReference, + val subtitle: TextReference, + val iconState: TangemIconUM, + val transactionTitle: TextReference, + val transactionCategory: TextReference, + val mcc: TextReference?, + val transactionAmount: String, + val localTransactionText: String?, + val label: TransactionLabelUM?, + val buttonState: ButtonState, + val dismiss: () -> Unit, +) + +internal data class TransactionLabelUM( + val transactionStateType: TransactionStateType, + val icon: TangemIconUM, + val title: TextReference, + val subtitle: TextReference? = null, +) + +internal enum class TransactionStateType { + Completed, + InProgress, + Rejected, + Reversed, +} + +@Immutable +internal sealed interface TangemPayTxHistoryDetailsUiState { + data class Legacy(val state: TangemPayTxHistoryDetailsUM) : TangemPayTxHistoryDetailsUiState + data class Redesign(val state: TangemPayTxHistoryDetailsUMV2) : TangemPayTxHistoryDetailsUiState +} + +internal data class TangemPayTxHistoryDetailsUiStates( + val legacy: TangemPayTxHistoryDetailsUM, + val redesign: TangemPayTxHistoryDetailsUMV2, +) { + fun toUiState(isRedesignEnabled: Boolean): TangemPayTxHistoryDetailsUiState { + return if (isRedesignEnabled) { + TangemPayTxHistoryDetailsUiState.Redesign(redesign) + } else { + TangemPayTxHistoryDetailsUiState.Legacy(legacy) + } + } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardPageModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardPageModel.kt index cae5f6b219..97dcffc0fd 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardPageModel.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardPageModel.kt @@ -21,6 +21,8 @@ import com.tangem.core.ui.format.bigdecimal.getJavaCurrencyByCode import com.tangem.core.ui.format.bigdecimal.optionalDecimals import com.tangem.core.ui.message.SnackbarMessage 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_arrow_refresh_20 import com.tangem.core.ui.test.TangemPayTestTags import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TokenReceiveConfig @@ -222,7 +224,7 @@ internal class TangemPayCardPageModel @Inject constructor( title = TextReference.Res(R.string.tangempay_card_details_reissue_card), onClick = ::onClickReissueCard, icon = TangemIconUM.Icon( - iconRes = CoreUiR.drawable.ic_replace_20, + imageVector = Icons.ic_arrow_refresh_20, tintReference = { TangemTheme.colors3.icon.primary }, diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayTxHistoryDetailsModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayTxHistoryDetailsModel.kt index e430f443af..ca8d041322 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayTxHistoryDetailsModel.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayTxHistoryDetailsModel.kt @@ -14,8 +14,9 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.features.tangempay.components.TangemPayTransactionBottomSheetComponent -import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM +import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUiStates import com.tangem.features.tangempay.model.transformers.TangemPayTxHistoryDetailsConverter +import com.tangem.features.tangempay.model.transformers.TangemPayTxHistoryDetailsConverterV2 import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch @@ -35,18 +36,8 @@ internal class TangemPayTxHistoryDetailsModel @Inject constructor( ) : Model() { private val params = paramsContainer.require() - val uiState: StateFlow - field = MutableStateFlow( - value = TangemPayTxHistoryDetailsConverter.convert( - value = TangemPayTxHistoryDetailsConverter.Input( - item = params.transaction, - isBalanceHidden = params.isBalanceHidden, - onExplorerClick = ::openExplorer, - onDisputeClick = { dispute(customerId = params.customerId) }, - onDismiss = ::dismiss, - ), - ), - ) + val uiState: StateFlow + field = MutableStateFlow(buildUiStates(isBalanceHidden = params.isBalanceHidden)) init { subscribeToBalanceHiding() @@ -58,10 +49,32 @@ internal class TangemPayTxHistoryDetailsModel @Inject constructor( private fun subscribeToBalanceHiding() { balanceHidingSettings.isBalanceHidden() - .onEach { isBalanceHidden -> uiState.update { it.copy(isBalanceHidden = isBalanceHidden) } } + .onEach { isBalanceHidden -> uiState.update { buildUiStates(isBalanceHidden) } } .launchIn(modelScope) } + private fun buildUiStates(isBalanceHidden: Boolean): TangemPayTxHistoryDetailsUiStates { + val converterInput = TangemPayTxHistoryDetailsConverter.Input( + item = params.transaction, + isBalanceHidden = isBalanceHidden, + onExplorerClick = ::openExplorer, + onDisputeClick = { dispute(customerId = params.customerId) }, + onDismiss = ::dismiss, + ) + return TangemPayTxHistoryDetailsUiStates( + legacy = TangemPayTxHistoryDetailsConverter.convert(converterInput), + redesign = TangemPayTxHistoryDetailsConverterV2.convert( + value = TangemPayTxHistoryDetailsConverterV2.Input( + item = converterInput.item, + isBalanceHidden = converterInput.isBalanceHidden, + onExplorerClick = converterInput.onExplorerClick, + onDisputeClick = converterInput.onDisputeClick, + onDismiss = converterInput.onDismiss, + ), + ), + ) + } + private fun openExplorer(txHash: String?) { txHash?.let(urlOpener::openUrlExternalBrowser) } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayTxHistoryDetailsConverter.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayTxHistoryDetailsConverter.kt index b15f718595..e4f3db8c2e 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayTxHistoryDetailsConverter.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayTxHistoryDetailsConverter.kt @@ -12,8 +12,8 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.utils.DateTimeFormatters import com.tangem.domain.visa.model.TangemPayTxHistoryItem import com.tangem.features.tangempay.details.impl.R +import com.tangem.features.tangempay.entity.ButtonState import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM -import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM.ButtonState import com.tangem.utils.StringsSigns import com.tangem.utils.converter.Converter import com.tangem.utils.extensions.isPositive diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayTxHistoryDetailsConverterV2.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayTxHistoryDetailsConverterV2.kt new file mode 100644 index 0000000000..7ce966bf8b --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayTxHistoryDetailsConverterV2.kt @@ -0,0 +1,322 @@ +package com.tangem.features.tangempay.model.transformers + +import com.tangem.core.ui.ds.image.TangemIconUM +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.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.TangemTheme +import com.tangem.core.ui.res.generated.icons.* +import com.tangem.core.ui.utils.DateTimeFormatters +import com.tangem.domain.visa.model.TangemPayTxHistoryItem +import com.tangem.features.tangempay.details.impl.R +import com.tangem.features.tangempay.entity.ButtonState +import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUMV2 +import com.tangem.features.tangempay.entity.TransactionLabelUM +import com.tangem.features.tangempay.entity.TransactionStateType +import com.tangem.utils.StringsSigns +import com.tangem.utils.converter.Converter +import com.tangem.utils.extensions.isPositive +import com.tangem.utils.extensions.isZero + +internal object TangemPayTxHistoryDetailsConverterV2 : + Converter { + private val dateFormatter = DateTimeFormatters.getBestFormatterBySkeleton("MMM dd yyyy, HH:mm") + + override fun convert(value: Input): TangemPayTxHistoryDetailsUMV2 { + val transaction = value.item + return TangemPayTxHistoryDetailsUMV2( + isBalanceHidden = value.isBalanceHidden, + title = transaction.extractBottomSheetTitle(), + subtitle = transaction.extractDate(), + iconState = transaction.extractIcon(), + transactionTitle = transaction.extractTransactionTitle(), + transactionCategory = transaction.extractTransactionCategory(), + mcc = transaction.extractMcc(), + transactionAmount = transaction.extractAmount(), + localTransactionText = transaction.extractLocalAmount(), + label = transaction.extractTransactionLabel(), + buttonState = value.extractButtonState(), + dismiss = value.onDismiss, + ) + } + + private fun TangemPayTxHistoryItem.extractDate(): TextReference { + val date = DateTimeFormatters.formatDate(this.date, dateFormatter) + return stringReference(date) + } + + private fun TangemPayTxHistoryItem.extractBottomSheetTitle(): TextReference { + return when (this) { + is TangemPayTxHistoryItem.Spend -> resourceReference(R.string.tangem_pay_purchase) + is TangemPayTxHistoryItem.Payment -> resourceReference(R.string.tangem_pay_withdrawal) + is TangemPayTxHistoryItem.Collateral -> when (this.type) { + TangemPayTxHistoryItem.Type.Deposit -> resourceReference(R.string.tangem_pay_deposit) + TangemPayTxHistoryItem.Type.Withdrawal -> resourceReference(R.string.tangem_pay_withdrawal) + } + is TangemPayTxHistoryItem.Fee -> resourceReference(R.string.tangem_pay_fee_title) + } + } + + private fun TangemPayTxHistoryItem.extractIcon(): TangemIconUM { + return when (this) { + is TangemPayTxHistoryItem.Collateral -> when (this.type) { + TangemPayTxHistoryItem.Type.Deposit -> TangemIconUM.Icon( + imageVector = Icons.ic_arrow_down_24, + tintReference = { + TangemTheme.colors3.icon.secondary + }, + ) + TangemPayTxHistoryItem.Type.Withdrawal -> TangemIconUM.Icon( + imageVector = Icons.ic_arrow_up_24, + tintReference = { + TangemTheme.colors3.icon.secondary + }, + ) + } + is TangemPayTxHistoryItem.Fee -> { + TangemIconUM.Icon( + iconRes = R.drawable.ic_percent_24, + tintReference = { + TangemTheme.colors3.icon.secondary + }, + ) + } + is TangemPayTxHistoryItem.Payment -> TangemIconUM.Icon( + imageVector = Icons.ic_arrow_up_24, + tintReference = { + TangemTheme.colors3.icon.secondary + }, + ) + is TangemPayTxHistoryItem.Spend -> { + val merchantIcon = this.enrichedMerchantIconUrl + if (merchantIcon != null) { + TangemIconUM.Url(merchantIcon, R.drawable.ic_category_24) + } else { + TangemIconUM.Icon( + iconRes = R.drawable.ic_category_24, + tintReference = { + TangemTheme.colors3.icon.secondary + }, + ) + } + } + } + } + + private fun TangemPayTxHistoryItem.extractTransactionTitle(): TextReference { + return when (this) { + is TangemPayTxHistoryItem.Spend -> stringReference(this.enrichedMerchantName ?: this.merchantName) + is TangemPayTxHistoryItem.Payment -> resourceReference(R.string.common_transfer) + is TangemPayTxHistoryItem.Collateral -> resourceReference(R.string.common_transfer) + is TangemPayTxHistoryItem.Fee -> { + this.description?.let(::stringReference) ?: resourceReference(R.string.tangem_pay_fee_subtitle) + } + } + } + + private fun TangemPayTxHistoryItem.extractTransactionCategory(): TextReference { + return when (this) { + is TangemPayTxHistoryItem.Fee -> resourceReference(R.string.tangem_pay_fee_subtitle) + is TangemPayTxHistoryItem.Payment -> resourceReference(R.string.common_transfer) + is TangemPayTxHistoryItem.Collateral -> resourceReference(R.string.common_transfer) + is TangemPayTxHistoryItem.Spend -> extractSpendCategory() + } + } + + private fun TangemPayTxHistoryItem.Spend.extractSpendCategory(): TextReference { + val merchantCategory = merchantCategory + val enrichedMerchantCategory = enrichedMerchantCategory + return when { + !merchantCategory.isNullOrEmpty() -> stringReference(merchantCategory) + !enrichedMerchantCategory.isNullOrEmpty() -> stringReference(enrichedMerchantCategory) + else -> resourceReference(R.string.tangem_pay_other) + } + } + + private fun TangemPayTxHistoryItem.extractMcc(): TextReference? { + return when (this) { + is TangemPayTxHistoryItem.Spend -> merchantCategoryCode + ?.takeIf { it.isNotEmpty() } + ?.let(::stringReference) + is TangemPayTxHistoryItem.Fee, + is TangemPayTxHistoryItem.Payment, + is TangemPayTxHistoryItem.Collateral, + -> null + } + } + + private fun TangemPayTxHistoryItem.extractAmount(): String { + return when (this) { + is TangemPayTxHistoryItem.Fee, + is TangemPayTxHistoryItem.Payment, + -> { + val amount = this.amount.format { + fiat( + fiatCurrencyCode = this@extractAmount.currency.currencyCode, + fiatCurrencySymbol = this@extractAmount.currency.symbol, + ) + } + StringsSigns.MINUS + amount + } + is TangemPayTxHistoryItem.Spend -> { + val amountPrefix = when { + this.amount.isZero() -> "" + this.status == TangemPayTxHistoryItem.Status.REVERSED -> StringsSigns.MINUS + this.status == TangemPayTxHistoryItem.Status.DECLINED || + this.amount.isPositive() -> StringsSigns.MINUS + else -> StringsSigns.PLUS + } + val amount = when (this.status) { + TangemPayTxHistoryItem.Status.DECLINED -> this.authorizedAmount + else -> this.amount + } + val formattedAmount = amount.abs().format { + fiat( + fiatCurrencyCode = this@extractAmount.currency.currencyCode, + fiatCurrencySymbol = this@extractAmount.currency.symbol, + ) + } + amountPrefix + formattedAmount + } + is TangemPayTxHistoryItem.Collateral -> { + val amountPrefix = when { + this.amount.isZero() -> "" + this.amount.isPositive() -> StringsSigns.PLUS + else -> StringsSigns.MINUS + } + val amount = this.amount.abs().format { + fiat( + fiatCurrencyCode = this@extractAmount.currency.currencyCode, + fiatCurrencySymbol = this@extractAmount.currency.symbol, + ) + } + amountPrefix + amount + } + } + } + + private fun TangemPayTxHistoryItem.extractLocalAmount(): String? { + return when (this) { + is TangemPayTxHistoryItem.Collateral, + is TangemPayTxHistoryItem.Fee, + is TangemPayTxHistoryItem.Payment, + -> null + is TangemPayTxHistoryItem.Spend -> { + val localCurrency = this.localCurrency + val localAmount = this.localAmount + if (localCurrency != null && localAmount != null && localCurrency != currency) { + val amountPrefix = when { + localAmount.isZero() -> "" + this.status == TangemPayTxHistoryItem.Status.DECLINED || + localAmount.isPositive() -> StringsSigns.MINUS + else -> StringsSigns.PLUS + } + val amount = localAmount.abs().format { + fiat( + fiatCurrencyCode = localCurrency.currencyCode, + fiatCurrencySymbol = localCurrency.symbol, + ).price() + } + amountPrefix + amount + } else { + null + } + } + } + } + + private fun TangemPayTxHistoryItem.extractTransactionLabel(): TransactionLabelUM? { + return when (this) { + is TangemPayTxHistoryItem.Payment, + is TangemPayTxHistoryItem.Collateral, + -> { + TransactionLabelUM( + transactionStateType = TransactionStateType.Completed, + icon = TangemIconUM.Icon( + imageVector = Icons.ic_success_24, + tintReference = { TangemTheme.colors3.icon.status.success }, + ), + title = resourceReference(R.string.tangem_pay_status_completed), + ) + } + is TangemPayTxHistoryItem.Fee -> TransactionLabelUM( + transactionStateType = TransactionStateType.Completed, + icon = TangemIconUM.Icon( + imageVector = Icons.ic_success_24, + tintReference = { TangemTheme.colors3.icon.status.success }, + ), + title = resourceReference(R.string.tangem_pay_status_completed), + subtitle = resourceReference(R.string.tangem_pay_transaction_fee_notification_text), + ) + is TangemPayTxHistoryItem.Spend -> when (this.status) { + TangemPayTxHistoryItem.Status.COMPLETED -> TransactionLabelUM( + transactionStateType = TransactionStateType.Completed, + icon = TangemIconUM.Icon( + imageVector = Icons.ic_success_24, + tintReference = { TangemTheme.colors3.icon.status.success }, + ), + title = resourceReference(R.string.tangem_pay_status_completed), + ) + TangemPayTxHistoryItem.Status.PENDING, + TangemPayTxHistoryItem.Status.RESERVED, + -> TransactionLabelUM( + transactionStateType = TransactionStateType.InProgress, + icon = TangemIconUM.Icon( + imageVector = Icons.ic_clock_24, + tintReference = { TangemTheme.colors3.icon.status.info }, + ), + title = resourceReference(R.string.tangem_pay_status_pending), + ) + TangemPayTxHistoryItem.Status.DECLINED -> TransactionLabelUM( + transactionStateType = TransactionStateType.Rejected, + icon = TangemIconUM.Icon( + iconRes = R.drawable.ic_warning_20, + tintReference = { TangemTheme.colors3.icon.status.error }, + ), + title = resourceReference(R.string.tangem_pay_status_declined), + subtitle = extractDeclinedSubtitle(), + ) + TangemPayTxHistoryItem.Status.REVERSED -> TransactionLabelUM( + transactionStateType = TransactionStateType.Reversed, + icon = TangemIconUM.Icon( + imageVector = Icons.ic_success_24, + tintReference = { TangemTheme.colors3.icon.status.success }, + ), + title = resourceReference(R.string.tangem_pay_status_reversed), + subtitle = resourceReference(R.string.tangem_pay_transaction_reversed_notification_text), + ) + TangemPayTxHistoryItem.Status.UNKNOWN -> null + } + } + } + + private fun TangemPayTxHistoryItem.Spend.extractDeclinedSubtitle(): TextReference { + return 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)), + ) + } + } + + private fun Input.extractButtonState(): ButtonState { + return ButtonState( + text = resourceReference(R.string.tangem_pay_get_help), + onClick = this.onDisputeClick, + ) + } + + data class Input( + val item: TangemPayTxHistoryItem, + val isBalanceHidden: Boolean, + val onExplorerClick: (String?) -> Unit, + val onDisputeClick: () -> Unit, + val onDismiss: () -> Unit, + ) +} \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangempayTxDetailsUi.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangempayTxDetailsUi.kt index 111edfb273..42f821bf8a 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangempayTxDetailsUi.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangempayTxDetailsUi.kt @@ -30,6 +30,7 @@ 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 +import com.tangem.features.tangempay.entity.ButtonState import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -115,10 +116,7 @@ internal fun TangemPayTxHistoryDetailsContent(state: TangemPayTxHistoryDetailsUM } @Composable -private fun ButtonsContainer( - buttons: ImmutableList, - modifier: Modifier = Modifier, -) { +private fun ButtonsContainer(buttons: ImmutableList, modifier: Modifier = Modifier) { Column( modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp), @@ -179,7 +177,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr ), notification = null, buttons = persistentListOf( - TangemPayTxHistoryDetailsUM.ButtonState( + ButtonState( text = resourceReference(R.string.tangem_pay_dispute), onClick = {}, ), @@ -210,7 +208,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr containerColor = themedColor { TangemColorPalette.Amaranth.copy(alpha = 0.1F) }, ), buttons = persistentListOf( - TangemPayTxHistoryDetailsUM.ButtonState( + ButtonState( text = resourceReference(R.string.tangem_pay_dispute), onClick = {}, ), @@ -232,7 +230,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr ), notification = null, buttons = persistentListOf( - TangemPayTxHistoryDetailsUM.ButtonState( + ButtonState( text = resourceReference(R.string.tangem_pay_dispute), onClick = {}, ), @@ -260,7 +258,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr containerColor = null, ), buttons = persistentListOf( - TangemPayTxHistoryDetailsUM.ButtonState( + ButtonState( text = resourceReference(R.string.tangem_pay_dispute), onClick = {}, ), @@ -279,7 +277,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr labelState = null, notification = null, buttons = persistentListOf( - TangemPayTxHistoryDetailsUM.ButtonState( + ButtonState( text = resourceReference(R.string.tangem_pay_get_help), onClick = {}, ), @@ -298,7 +296,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr labelState = null, notification = null, buttons = persistentListOf( - TangemPayTxHistoryDetailsUM.ButtonState( + ButtonState( text = resourceReference(R.string.tangem_pay_get_help), onClick = {}, ), diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangempayTxDetailsUiV2.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangempayTxDetailsUiV2.kt new file mode 100644 index 0000000000..3629c40907 --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangempayTxDetailsUiV2.kt @@ -0,0 +1,389 @@ +package com.tangem.features.tangempay.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.TextAutoSize +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Devices +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import com.tangem.core.ui.components.SpacerW +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType +import com.tangem.core.ui.ds.image.TangemIcon +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.topbar.TangemTopBar +import com.tangem.core.ui.ds.topbar.TangemTopBarType +import com.tangem.core.ui.ds2.button.TangemButton +import com.tangem.core.ui.ds2.row.TangemRow +import com.tangem.core.ui.ds2.row.TangemRowContentLead +import com.tangem.core.ui.ds2.row.TangemRowText +import com.tangem.core.ui.ds2.row.TangemRowTextRole +import com.tangem.core.ui.extensions.orMaskWithStars +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreviewRedesign +import com.tangem.core.ui.res.generated.icons.Icons +import com.tangem.core.ui.res.generated.icons.ic_arrow_down_24 +import com.tangem.features.tangempay.details.impl.R +import com.tangem.features.tangempay.entity.ButtonState +import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUMV2 +import com.tangem.features.tangempay.entity.TransactionLabelUM +import com.tangem.features.tangempay.entity.TransactionStateType + +@Suppress("LongMethod") +@Composable +internal fun TangemPayTxHistoryDetailsContentV2(state: TangemPayTxHistoryDetailsUMV2) { + TangemBottomSheet( + config = TangemBottomSheetConfig( + isShown = true, + onDismissRequest = state.dismiss, + content = TangemBottomSheetConfigContent.Empty, + ), + type = TangemBottomSheetType.Modal, + containerColor = TangemTheme.colors3.bg.secondary, + title = { + TangemTopBar( + title = state.title, + subtitle = state.subtitle, + type = TangemTopBarType.BottomSheet, + endContent = { + TangemButton( + iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_close_24), + onClick = state.dismiss, + size = TangemButton.Size.X11, + variant = TangemButton.Variant.Material, + ) + }, + ) + }, + content = { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(horizontal = TangemTheme.dimens2.x4) + .padding(bottom = TangemTheme.dimens2.x4), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + TransactionIcon( + iconState = state.iconState, + modifier = Modifier.padding(top = TangemTheme.dimens2.x12), + ) + Text( + modifier = Modifier.padding(top = TangemTheme.dimens2.x6), + text = state.transactionAmount.orMaskWithStars(state.isBalanceHidden), + style = TangemTheme.typography3.display.medium, + color = TangemTheme.colors3.text.primary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + autoSize = TextAutoSize.StepBased( + minFontSize = TangemTheme.typography3.body.medium.fontSize, + maxFontSize = TangemTheme.typography3.display.medium.fontSize, + ), + ) + TransactionSecondaryLine( + state = state, + modifier = Modifier.padding(top = TangemTheme.dimens2.x1), + ) + if (state.label != null) { + TransactionLabel( + label = state.label, + modifier = Modifier + .padding(top = TangemTheme.dimens2.x12) + .fillMaxWidth(), + ) + } + TransactionDetailsBlock( + state = state, + modifier = Modifier + .padding(top = TangemTheme.dimens2.x4, bottom = TangemTheme.dimens2.x2) + .fillMaxWidth(), + ) + TangemButton( + modifier = Modifier + .padding(vertical = TangemTheme.dimens2.x4) + .fillMaxWidth(), + text = state.buttonState.text, + onClick = state.buttonState.onClick, + size = TangemButton.Size.X12, + ) + } + }, + ) +} + +@Composable +private fun TransactionIcon(iconState: TangemIconUM, modifier: Modifier = Modifier) { + Box( + modifier = modifier + .size(TangemTheme.dimens2.x20) + .clip(CircleShape) + .background(TangemTheme.colors3.bg.opaque.primary), + contentAlignment = Alignment.Center, + ) { + TangemIcon( + tangemIconUM = iconState, + modifier = Modifier.size( + if (iconState is TangemIconUM.Icon) { + TangemTheme.dimens2.x12 + } else { + TangemTheme.dimens2.x20 + }, + ), + ) + } +} + +@Composable +private fun TransactionSecondaryLine(state: TangemPayTxHistoryDetailsUMV2, modifier: Modifier = Modifier) { + val secondaryText = when { + state.localTransactionText != null -> { + buildString { + append(state.localTransactionText.orMaskWithStars(state.isBalanceHidden)) + append(" · ") + append(state.transactionTitle.resolveReference()) + } + } + else -> state.transactionTitle.resolveReference() + } + Text( + modifier = modifier, + text = secondaryText, + style = TangemTheme.typography3.subheading.medium, + color = TangemTheme.colors3.text.secondary, + textAlign = TextAlign.Center, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + ) +} + +@Composable +internal fun TransactionLabel(label: TransactionLabelUM, modifier: Modifier = Modifier) { + val (backgroundColor, textColor) = when (label.transactionStateType) { + TransactionStateType.Completed, + TransactionStateType.Reversed, + -> { + TangemTheme.colors3.bg.status.successSubtle to TangemTheme.colors3.text.status.success + } + TransactionStateType.InProgress -> { + TangemTheme.colors3.bg.status.infoSubtle to TangemTheme.colors3.text.status.info + } + TransactionStateType.Rejected -> { + TangemTheme.colors3.bg.status.errorSubtle to TangemTheme.colors3.text.status.error + } + } + + Row( + modifier = modifier + .background( + color = backgroundColor, + shape = RoundedCornerShape(TangemTheme.dimens3.borderRadius.b250), + ) + .padding( + vertical = TangemTheme.dimens2.x3, + horizontal = TangemTheme.dimens2.x4, + ), + ) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1), + ) { + Text( + text = label.title.resolveReference(), + style = TangemTheme.typography3.body.medium, + color = textColor, + ) + + label.subtitle?.let { text -> + Text( + text = text.resolveReference(), + style = TangemTheme.typography3.caption.medium, + color = textColor, + ) + } + } + + SpacerW(TangemTheme.dimens2.x1) + + TangemIcon( + tangemIconUM = label.icon, + modifier = Modifier.size(TangemTheme.dimens2.x5), + ) + } +} + +@Composable +private fun TransactionDetailsBlock(state: TangemPayTxHistoryDetailsUMV2, modifier: Modifier = Modifier) { + Column(modifier = modifier) { + TangemRow( + divider = state.mcc != null, + contentLead = TangemRowContentLead.Start, + titleSlot = { + TangemRowText( + text = resourceReference(R.string.tangem_pay_transaction_details_category), + role = TangemRowTextRole.Title, + ) + }, + valueSlot = { + TangemRowText( + text = state.transactionCategory, + role = TangemRowTextRole.Value, + ) + }, + ) + if (state.mcc != null) { + TangemRow( + contentLead = TangemRowContentLead.Start, + titleSlot = { + TangemRowText( + text = resourceReference(R.string.tangem_pay_transaction_details_mcc), + role = TangemRowTextRole.Title, + ) + }, + valueSlot = { + TangemRowText( + text = state.mcc, + role = TangemRowTextRole.Value, + ) + }, + ) + } + } +} + +@Preview(device = Devices.PIXEL_7_PRO) +@Preview(device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun TangemPayTxHistoryDetailsContentPreview( + @PreviewParameter(TangemPayTxHistoryDetailsUMProviderV2::class) state: TangemPayTxHistoryDetailsUMV2, +) { + TangemThemePreviewRedesign { + TangemPayTxHistoryDetailsContentV2(state = state) + } +} + +private class TangemPayTxHistoryDetailsUMProviderV2 : + CollectionPreviewParameterProvider( + listOf( + TangemPayTxHistoryDetailsUMV2( + isBalanceHidden = true, + title = resourceReference(R.string.tangem_pay_purchase), + subtitle = stringReference("12 June 2026, 12:40"), + iconState = TangemIconUM.Icon(iconRes = R.drawable.ic_category_24), + transactionTitle = stringReference("Starbucks"), + transactionCategory = stringReference("Food and drinks"), + mcc = stringReference("5814"), + transactionAmount = "-$5.86", + localTransactionText = null, + label = TransactionLabelUM( + transactionStateType = TransactionStateType.InProgress, + icon = TangemIconUM.Icon(iconRes = com.tangem.core.ui.R.drawable.ic_clock_24), + title = resourceReference(R.string.tangem_pay_status_pending), + ), + buttonState = ButtonState( + text = resourceReference(R.string.tangem_pay_get_help), + onClick = {}, + ), + dismiss = {}, + ), + TangemPayTxHistoryDetailsUMV2( + isBalanceHidden = true, + title = resourceReference(R.string.tangem_pay_purchase), + subtitle = stringReference("12 June 2026, 12:40"), + iconState = TangemIconUM.Icon(iconRes = R.drawable.ic_category_24), + transactionTitle = stringReference("NuCaloric"), + transactionCategory = stringReference("Groceries"), + mcc = stringReference("0000"), + transactionAmount = "-$820.52", + localTransactionText = "-€696,52", + label = TransactionLabelUM( + transactionStateType = TransactionStateType.Rejected, + icon = TangemIconUM.Icon(iconRes = R.drawable.ic_token_info_24), + title = resourceReference(R.string.tangem_pay_status_declined), + subtitle = stringReference("Reason: account credit limit exceeded"), + ), + buttonState = ButtonState( + text = resourceReference(R.string.tangem_pay_get_help), + onClick = {}, + ), + dismiss = {}, + ), + TangemPayTxHistoryDetailsUMV2( + isBalanceHidden = true, + title = resourceReference(R.string.tangem_pay_purchase), + subtitle = stringReference("12 June 2026, 12:40"), + iconState = TangemIconUM.Icon(iconRes = R.drawable.ic_category_24), + transactionTitle = stringReference("Starbucks"), + transactionCategory = stringReference("Food and drinks"), + mcc = null, + transactionAmount = "-$5.86", + localTransactionText = "€ 5.36", + label = TransactionLabelUM( + transactionStateType = TransactionStateType.Completed, + icon = TangemIconUM.Empty, + title = resourceReference(R.string.tangem_pay_status_completed), + ), + buttonState = ButtonState( + text = resourceReference(R.string.tangem_pay_get_help), + onClick = {}, + ), + dismiss = {}, + ), + TangemPayTxHistoryDetailsUMV2( + isBalanceHidden = false, + title = resourceReference(R.string.tangem_pay_fee_title), + subtitle = stringReference("12 June 2026, 12:40"), + iconState = TangemIconUM.Icon(iconRes = R.drawable.ic_percent_24), + transactionTitle = stringReference("Service fees"), + transactionCategory = stringReference("Service fees"), + mcc = null, + transactionAmount = "-$5.86", + localTransactionText = null, + label = TransactionLabelUM( + transactionStateType = TransactionStateType.Completed, + icon = TangemIconUM.Icon(iconRes = R.drawable.ic_token_info_24), + title = resourceReference(R.string.tangem_pay_fee_title), + subtitle = resourceReference(R.string.tangem_pay_transaction_fee_notification_text), + ), + buttonState = ButtonState( + text = resourceReference(R.string.tangem_pay_get_help), + onClick = {}, + ), + dismiss = {}, + ), + TangemPayTxHistoryDetailsUMV2( + isBalanceHidden = false, + title = resourceReference(R.string.tangem_pay_deposit), + subtitle = stringReference("12 June 2026, 12:40"), + iconState = TangemIconUM.Icon(imageVector = Icons.ic_arrow_down_24), + transactionTitle = resourceReference(R.string.common_transfer), + transactionCategory = resourceReference(R.string.common_transfer), + mcc = null, + transactionAmount = "+$20", + localTransactionText = null, + label = null, + buttonState = ButtonState( + text = resourceReference(R.string.tangem_pay_get_help), + onClick = {}, + ), + dismiss = {}, + ), + ), + ) \ No newline at end of file