Updated on 2026-08-14
This commit is contained in:
parent
f6f3aabbfc
commit
cff379462f
12 changed files with 848 additions and 71 deletions
|
|
@ -1677,7 +1677,7 @@
|
|||
<string name="tangem_pay_card_details_unable_to_rename_card_title">Unable to rename card</string>
|
||||
<string name="tangem_pay_card_frozen">Card frozen</string>
|
||||
<string name="tangem_pay_card_payment">Card payment</string>
|
||||
<string name="tangem_pay_close_card_popup_description">It will disappear from payment account</string>
|
||||
<string name="tangem_pay_close_card_popup_description">It will disappear from the app</string>
|
||||
<string name="tangem_pay_close_card_popup_primary_button_title">Close card</string>
|
||||
<string name="tangem_pay_close_card_popup_secondary_button_title">Go back</string>
|
||||
<string name="tangem_pay_close_card_popup_title">Close your card?</string>
|
||||
|
|
@ -1698,6 +1698,7 @@
|
|||
<string name="tangem_pay_history_item_spend_mcc">MCC %s</string>
|
||||
<string name="tangem_pay_other">Other</string>
|
||||
<string name="tangem_pay_pin_code_title">PIN-code</string>
|
||||
<string name="tangem_pay_purchase">Purchase</string>
|
||||
<string name="tangem_pay_rooted_device_subtitle">Unable to use on rooted devices</string>
|
||||
<string name="tangem_pay_status_completed">Completed</string>
|
||||
<string name="tangem_pay_status_declined">Declined</string>
|
||||
|
|
@ -1706,6 +1707,8 @@
|
|||
<string name="tangem_pay_terms_fees_limits">Terms, Fees & Limits</string>
|
||||
<string name="tangem_pay_terms_limits">Terms and fees</string>
|
||||
<string name="tangem_pay_transaction_declined_notification_text">The bank rejected this transaction request.</string>
|
||||
<string name="tangem_pay_transaction_details_category">Category</string>
|
||||
<string name="tangem_pay_transaction_details_mcc">MCC</string>
|
||||
<string name="tangem_pay_transaction_fee_notification_text">A fee is charged in accordance with the service tariffs</string>
|
||||
<string name="tangem_pay_transaction_reversed_notification_text">The transaction was partially or fully reversed by the merchant</string>
|
||||
<string name="tangem_pay_unfreeze_card_alert_body">Keep using your money. You can freeze anytime.</string>
|
||||
|
|
@ -1824,6 +1827,24 @@
|
|||
<string name="tangempay_kyc_rejected_description_span">your profile.</string>
|
||||
<string name="tangempay_maximum_cards_issued_description">You can have up to 3 cards. Delete one to add a new card.</string>
|
||||
<string name="tangempay_maximum_cards_issued_title">Maximum Cards Issued</string>
|
||||
<string name="tangempay_newonboard_Q1_body">Yes – to operate a regulated Visa card, identity verification is mandatory. KYC is handled by Sumsub (compliance partner).</string>
|
||||
<string name="tangempay_newonboard_Q1_title">Do I have to share my docs?</string>
|
||||
<string name="tangempay_newonboard_Q2_body">No. KYC applies only to the Tangem Pay account. Your Tangem Wallet itself remains a separate, self-custodial, non-KYC environment.</string>
|
||||
<string name="tangempay_newonboard_Q2_title">Does the KYC associate with my wallet?</string>
|
||||
<string name="tangempay_newonboard_Q3_body">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.</string>
|
||||
<string name="tangempay_newonboard_Q3_title">Who stores my personal data and how is it protected?</string>
|
||||
<string name="tangempay_newonboard_Q4_body">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.</string>
|
||||
<string name="tangempay_newonboard_Q4_title">What crypto can I spend?</string>
|
||||
<string name="tangempay_newonboard_body">Spend crypto anywhere — no banks, no middlemen, no exchanges. The power of self-custody meets everyday payments.</string>
|
||||
<string name="tangempay_newonboard_bottomleft_body">Buy online and via Apple Pay</string>
|
||||
<string name="tangempay_newonboard_bottomleft_title">Accepted worldwide</string>
|
||||
<string name="tangempay_newonboard_bottomright_body">Use anywhere in the world</string>
|
||||
<string name="tangempay_newonboard_bottomright_title">FX fee is just 1%</string>
|
||||
<string name="tangempay_newonboard_title">Get your Tangem Pay Card</string>
|
||||
<string name="tangempay_newonboard_topleft_body">Pay what you see</string>
|
||||
<string name="tangempay_newonboard_topleft_title">No purchase fees, \n1 USDC = 1 USD</string>
|
||||
<string name="tangempay_newonboard_topright_body">No surprises</string>
|
||||
<string name="tangempay_newonboard_topright_title">$0 monthly fee\n$0 topup fee</string>
|
||||
<string name="tangempay_onboarding_banner_description">Get your free Tangem Visa virtual card</string>
|
||||
<string name="tangempay_onboarding_banner_title">Use USDC for everyday payments</string>
|
||||
<string name="tangempay_onboarding_get_card_button_text">Get card</string>
|
||||
|
|
@ -1852,16 +1873,16 @@
|
|||
<string name="tangempay_reissue_card_title">Replace your card?</string>
|
||||
<string name="tangempay_service_unavailable_description">We’re fixing a technical issue. Please try again later.</string>
|
||||
<string name="tangempay_service_unavailable_title">Service temporarily unavailable</string>
|
||||
<string name="tangempay_service_unreachable_try_later">Unable to display details. However, card payments are still working.</string>
|
||||
<string name="tangempay_service_unreachable_try_later">The service is currently unreachable. Please try again later.</string>
|
||||
<string name="tangempay_set_pin_code">Set \nPIN code</string>
|
||||
<string name="tangempay_status_deactivated">Card deactivated</string>
|
||||
<string name="tangempay_status_deactivated">Account closed</string>
|
||||
<string name="tangempay_status_replacing">Replacing your card</string>
|
||||
<string name="tangempay_sync_needed">Use your card or ring to renew session</string>
|
||||
<string name="tangempay_sync_needed_body">Use your card or ring to renew session</string>
|
||||
<string name="tangempay_sync_needed_button">Renew session</string>
|
||||
<string name="tangempay_sync_needed_title">Payment account session expired</string>
|
||||
<string name="tangempay_tangem_visa_card">Use USDC for everyday payments</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay is temporarily unreachable</string>
|
||||
<string name="tangempay_temporarily_unavailable">Tangem Pay is temporarily unavailable</string>
|
||||
<string name="tangempay_title">Tangem Pay</string>
|
||||
<string name="tangempay_topup_receive_body">Send USDC Polygon to your account’s address </string>
|
||||
<string name="tangempay_topup_receive_title">From another wallet or exchange</string>
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
<!--
|
||||
~ Copyright (C) 2026 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M15.75,9.25C16.164,9.25 16.5,9.586 16.5,10C16.5,13.59 13.59,16.5 10,16.5C7.986,16.5 6.19,15.581 5,14.144V14.791C5,15.205 4.664,15.541 4.25,15.541C3.836,15.541 3.5,15.205 3.5,14.791V12.396C3.5,11.981 3.836,11.646 4.25,11.646H6.646C7.06,11.646 7.395,11.981 7.396,12.396C7.396,12.81 7.06,13.146 6.646,13.146H6.123C7.04,14.276 8.434,15 10,15C12.762,15 15,12.762 15,10C15,9.586 15.336,9.25 15.75,9.25Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M10,3.5C12.014,3.5 13.81,4.419 15,5.855V5.208C15,4.794 15.336,4.458 15.75,4.458C16.164,4.458 16.5,4.794 16.5,5.208V7.604C16.5,8.018 16.164,8.354 15.75,8.354H13.354C12.94,8.353 12.604,8.018 12.604,7.604C12.605,7.19 12.941,6.854 13.354,6.854H13.876C12.96,5.723 11.565,5 10,5C7.238,5 5,7.238 5,10C5,10.414 4.664,10.75 4.25,10.75C3.836,10.75 3.5,10.414 3.5,10C3.5,6.41 6.41,3.5 10,3.5Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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<TangemPayTransactionBottomSheetComponent.Params>()
|
||||
val uiState: StateFlow<TangemPayTxHistoryDetailsUM>
|
||||
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<TangemPayTxHistoryDetailsUiStates>
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<TangemPayTxHistoryDetailsConverterV2.Input, TangemPayTxHistoryDetailsUMV2> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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<TangemPayTxHistoryDetailsUM.ButtonState>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
private fun ButtonsContainer(buttons: ImmutableList<ButtonState>, 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 = {},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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<TangemBottomSheetConfigContent.Empty>(
|
||||
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<TangemPayTxHistoryDetailsUMV2>(
|
||||
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 = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue