Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-06 14:09:26 +05:00
parent 5d853ea8b8
commit 19d6b9748e
43 changed files with 414 additions and 384 deletions

View file

@ -649,10 +649,7 @@ internal class ChildFactory @Inject constructor(
is AppRoute.TangemPayDetails -> {
createComponentChild(
context = context,
params = TangemPayDetailsContainerComponent.Params(
userWalletId = route.userWalletId,
config = route.config,
),
params = TangemPayDetailsContainerComponent.Params(initialStatus = route.status),
componentFactory = tangemPayDetailsContainerComponentFactory,
)
}

View file

@ -17,6 +17,7 @@ import com.tangem.domain.markets.PreselectedTokenDetailsSection
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.earn.PreselectedEarnType
import com.tangem.domain.models.scan.ScanResponse
@ -24,7 +25,6 @@ import com.tangem.domain.models.serialization.SerializedBigDecimal
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.staking.model.StakingIntegrationID
import com.tangem.domain.tokens.model.details.NavigationAction
import kotlinx.serialization.Serializable
@ -450,9 +450,8 @@ sealed class AppRoute(val path: String) : Route {
@Serializable
data class TangemPayDetails(
val userWalletId: UserWalletId,
val config: TangemPayDetailsConfig,
) : AppRoute(path = "/tangem_pay_details/${userWalletId.stringValue}")
val status: AccountStatus.Payment,
) : AppRoute(path = "/tangem_pay_details/${status.account}")
@Serializable
data class TangemPayOnboarding(

View file

@ -58,6 +58,7 @@ sealed interface PaymentAccountStatusValueDM {
data class DeactivatedAccount(
@Json(name = "deactivated_account") val marker: Boolean = true,
@Json(name = "fiat_balance") val fiatBalance: FiatBalanceDM,
@Json(name = "crypto_balance") val cryptoBalance: CryptoBalanceDM,
) : PaymentAccountStatusValueDM
@JsonClass(generateAdapter = true)

View file

@ -59,6 +59,7 @@ internal class PaymentAccountStatusValueDMConverter @Inject constructor(
is PaymentAccountStatusValue.Empty -> PaymentAccountStatusValueDM.Empty()
is PaymentAccountStatusValue.Deactivated -> PaymentAccountStatusValueDM.DeactivatedAccount(
fiatBalance = value.fiatBalance.toDM(),
cryptoBalance = value.cryptoBalance.toDM(),
)
// Transient statuses are not persisted
is PaymentAccountStatusValue.Loading,
@ -70,6 +71,7 @@ internal class PaymentAccountStatusValueDMConverter @Inject constructor(
}
fun convertBack(userWalletId: UserWalletId, value: PaymentAccountStatusValueDM?): PaymentAccountStatusValue {
val cryptoCurrency = tangemPayCurrencyFactory.create(userWalletId)
return when (value) {
is PaymentAccountStatusValueDM.Empty -> PaymentAccountStatusValue.Empty
is PaymentAccountStatusValueDM.NotCreated -> PaymentAccountStatusValue.NotCreated
@ -86,7 +88,7 @@ internal class PaymentAccountStatusValueDMConverter @Inject constructor(
depositAddress = value.depositAddress,
fiatBalance = value.fiatBalance.toDomain(),
cryptoBalance = value.cryptoBalance.toDomain(),
cryptoCurrency = tangemPayCurrencyFactory.create(userWalletId),
cryptoCurrency = cryptoCurrency,
cards = value.cards.map { card ->
TangemPayCard(
id = card.id,
@ -113,6 +115,8 @@ internal class PaymentAccountStatusValueDMConverter @Inject constructor(
is PaymentAccountStatusValueDM.DeactivatedAccount -> PaymentAccountStatusValue.Deactivated(
source = StatusSource.CACHE,
fiatBalance = value.fiatBalance.toDomain(),
cryptoBalance = value.cryptoBalance.toDomain(),
cryptoCurrency = cryptoCurrency,
)
null -> PaymentAccountStatusValue.Error.Unavailable
}

View file

@ -250,6 +250,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
val isDeactivated = productInstance?.status == CustomerInfo.ProductInstance.Status.DEACTIVATED
val isFormer = state == CustomerInfo.State.FORMER
val fiatBalance = fiatBalance
val cryptoBalance = cryptoBalance
return when {
kycStatus != KycStatus.APPROVED && !customerId.isNullOrEmpty() -> {
@ -259,10 +260,12 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
customerId = requireNotNull(customerId) { "CustomerId must not be null" },
)
}
fiatBalance != null && (isDeactivated || isFormer) -> {
fiatBalance != null && cryptoBalance != null && (isDeactivated || isFormer) -> {
PaymentAccountStatusValue.Deactivated(
source = StatusSource.ACTUAL,
fiatBalance = fiatBalance,
cryptoBalance = cryptoBalance,
cryptoCurrency = tangemPayCurrencyFactory.create(userWalletId),
)
}
cardInfo != null && productInstance != null && !customerId.isNullOrEmpty() -> convertToContentState(

View file

@ -2,38 +2,32 @@ package com.tangem.data.pay.repository
import arrow.core.Either
import arrow.core.flatMap
import arrow.core.getOrElse
import arrow.core.left
import arrow.core.right
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.data.pay.store.PaymentAccountStatusesStore
import com.tangem.data.pay.util.CustomerInfoConverter
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.models.request.DeeplinkValidityRequest
import com.tangem.datasource.api.pay.models.request.OrderRequest
import com.tangem.datasource.api.pay.models.request.SetTangemPayEnabledRequest
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
import com.tangem.datasource.api.pay.models.response.FiatBalance
import com.tangem.datasource.api.pay.models.response.OrderResponse
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.CardDisplayName
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.kyc.KycStatus
import com.tangem.domain.models.pay.TangemPayCardLimit
import com.tangem.domain.models.pay.TangemPayCardLimitPeriod
import com.tangem.domain.models.pay.TangemPayEligibilityType
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.datasource.TangemPayAuthDataSource
import com.tangem.domain.pay.model.CustomerInfo
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import java.util.concurrent.ConcurrentHashMap
@ -99,10 +93,10 @@ internal class DefaultOnboardingRepository @Inject constructor(
override suspend fun getCustomerInfo(userWalletId: UserWalletId): Either<VisaApiError, CustomerInfo> {
return requestHelper.performRequest(userWalletId) { authHeader -> tangemPayApi.getCustomerMe(authHeader) }
.flatMap { response ->
val result = response.result
val status = result?.productInstance?.status
val result = response.result ?: return@flatMap VisaApiError.UnknownWithoutCode.left()
val status = result.productInstance?.status
val isDeactivated = status == CustomerMeResponse.ProductInstance.Status.DEACTIVATED
val isFormer = result?.state?.let { CustomerInfo.State.fromString(it) } == CustomerInfo.State.FORMER
val isFormer = result.state.let { CustomerInfo.State.fromString(it) } == CustomerInfo.State.FORMER
if (isDeactivated || isFormer) {
tangemPayStorage.storeIsTangemPayDeactivated(userWalletId)
}
@ -166,70 +160,16 @@ internal class DefaultOnboardingRepository @Inject constructor(
@Suppress("ComplexCondition")
private suspend fun getCustomerInfo(
userWalletId: UserWalletId,
response: CustomerMeResponse.Result?,
response: CustomerMeResponse.Result,
): CustomerInfo {
val kycStatus = KycStatus.fromString(status = response?.kyc?.status)
sendKycAnalytics(kycStatus)
val customerInfo = CustomerInfoConverter.convert(response)
sendKycAnalytics(customerInfo.kycStatus)
val card = response?.card
val fiatBalance = response?.balance?.fiat
val cryptoBalance = response?.balance?.crypto
val paymentAccount = response?.paymentAccount
val cardInfo = if (paymentAccount != null && card != null && fiatBalance != null && cryptoBalance != null) {
CardInfo(
lastFourDigits = card.cardNumberEnd,
balance = fiatBalance.availableBalance,
currencyCode = fiatBalance.currency,
depositAddress = response.depositAddress,
isPinSet = response.card?.isPinSet == true,
fiatBalance = fiatBalance.toDomain(),
cryptoBalance = PaymentAccountStatusValue.CryptoBalance(
id = cryptoBalance.id,
chainId = cryptoBalance.chainId.toLong(),
depositAddress = cryptoBalance.depositAddress.orEmpty(),
tokenContractAddress = cryptoBalance.tokenContractAddress,
balance = cryptoBalance.balance,
),
)
} else {
null
customerInfo.productInstance?.let { instance ->
cardFrozenStateStore.store(key = instance.cardId, value = instance.frozenState)
}
val productInstance = response?.productInstance?.let { instance ->
val cardFrozenState = when (instance.status) {
CustomerMeResponse.ProductInstance.Status.ACTIVE -> TangemPayCardFrozenState.Unfrozen
else -> TangemPayCardFrozenState.Frozen
}
cardFrozenStateStore.store(key = instance.cardId, value = cardFrozenState)
val displayName = instance.displayName?.ifEmpty { null }
ProductInstance(
id = instance.id,
cardId = instance.cardId,
frozenState = cardFrozenState,
status = instance.status.toDomain(),
displayName = if (displayName != null) CardDisplayName(displayName).getOrElse { null } else null,
actualCardLimit = instance.actualCardLimit?.parseCardLimit(),
adminCardLimit = instance.adminCardLimit?.parseCardLimit(),
)
}
return CustomerInfo(
customerId = response?.id,
productInstance = productInstance,
kycStatus = kycStatus,
cardInfo = cardInfo,
state = response?.state?.let { CustomerInfo.State.fromString(it) } ?: CustomerInfo.State.UNDEFINED,
fiatBalance = fiatBalance?.toDomain(),
).also {
lastFetchedCustomerInfoMap[userWalletId] = it
}
}
private fun CustomerMeResponse.CardLimit.parseCardLimit(): TangemPayCardLimit {
return TangemPayCardLimit(
amount = amount,
period = TangemPayCardLimitPeriod.fromString(periodType),
)
return customerInfo.also { lastFetchedCustomerInfoMap[userWalletId] = it }
}
private fun sendKycAnalytics(kycStatus: KycStatus) {
@ -308,24 +248,4 @@ internal class DefaultOnboardingRepository @Inject constructor(
setHideMainOnboardingBanner(userWalletId)
}
}
}
private fun FiatBalance.toDomain() = PaymentAccountStatusValue.FiatBalance(
availableBalance = availableBalance,
currency = currency,
)
private fun CustomerMeResponse.ProductInstance.Status.toDomain() = when (this) {
CustomerMeResponse.ProductInstance.Status.NEW -> ProductInstance.Status.NEW
CustomerMeResponse.ProductInstance.Status.READY_FOR_MANUFACTURING -> ProductInstance.Status.READY_FOR_MANUFACTURING
CustomerMeResponse.ProductInstance.Status.MANUFACTURING -> ProductInstance.Status.MANUFACTURING
CustomerMeResponse.ProductInstance.Status.SENT_TO_DELIVERY -> ProductInstance.Status.SENT_TO_DELIVERY
CustomerMeResponse.ProductInstance.Status.DELIVERED -> ProductInstance.Status.DELIVERED
CustomerMeResponse.ProductInstance.Status.ACTIVATING -> ProductInstance.Status.ACTIVATING
CustomerMeResponse.ProductInstance.Status.ACTIVE -> ProductInstance.Status.ACTIVE
CustomerMeResponse.ProductInstance.Status.BLOCKED -> ProductInstance.Status.BLOCKED
CustomerMeResponse.ProductInstance.Status.DEACTIVATING -> ProductInstance.Status.DEACTIVATING
CustomerMeResponse.ProductInstance.Status.DEACTIVATED -> ProductInstance.Status.DEACTIVATED
CustomerMeResponse.ProductInstance.Status.CANCELED -> ProductInstance.Status.CANCELED
CustomerMeResponse.ProductInstance.Status.UNKNOWN -> ProductInstance.Status.UNKNOWN
}

View file

@ -11,6 +11,7 @@ import com.tangem.datasource.api.pay.models.response.WithdrawResponse
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayWithdrawExchangeState
import com.tangem.domain.pay.TangemPayWithdrawState
import com.tangem.domain.pay.WithdrawalResult
@ -222,13 +223,13 @@ internal class DefaultTangemPayWithdrawRepository @Inject constructor(
}
}
override suspend fun hasWithdrawOrder(userWallet: UserWallet): Boolean {
val orderId = tangemPayStorage.getActiveWithdrawOrderId(userWallet.walletId)
override suspend fun hasWithdrawOrder(userWalletId: UserWalletId): Boolean {
val orderId = tangemPayStorage.getActiveWithdrawOrderId(userWalletId)
if (orderId.isNullOrEmpty()) return false
val orderData = orderRepository.getOrderData(userWalletId = userWallet.walletId, orderId = orderId).getOrNull()
val orderData = orderRepository.getOrderData(userWalletId = userWalletId, orderId = orderId).getOrNull()
val isActive = orderData?.status == OrderStatus.NEW || orderData?.status == OrderStatus.PROCESSING
if (!isActive) {
tangemPayStorage.deleteActiveWithdrawOrder(userWalletId = userWallet.walletId)
tangemPayStorage.deleteActiveWithdrawOrder(userWalletId = userWalletId)
}
return isActive
}

View file

@ -0,0 +1,103 @@
package com.tangem.data.pay.util
import arrow.core.getOrElse
import com.tangem.datasource.api.pay.models.response.CryptoBalance
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
import com.tangem.datasource.api.pay.models.response.FiatBalance
import com.tangem.domain.models.account.CardDisplayName
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.kyc.KycStatus
import com.tangem.domain.models.pay.TangemPayCardLimit
import com.tangem.domain.models.pay.TangemPayCardLimitPeriod
import com.tangem.domain.pay.model.CustomerInfo
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
import com.tangem.domain.pay.model.CustomerInfo.ProductInstance.Status
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.utils.converter.Converter
internal object CustomerInfoConverter : Converter<CustomerMeResponse.Result, CustomerInfo> {
@Suppress("ComplexCondition")
override fun convert(value: CustomerMeResponse.Result): CustomerInfo {
val kycStatus = KycStatus.fromString(status = value.kyc?.status)
val card = value.card
val fiatBalance = value.balance?.fiat
val cryptoBalance = value.balance?.crypto
val paymentAccount = value.paymentAccount
val cardInfo = if (paymentAccount != null && card != null && fiatBalance != null && cryptoBalance != null) {
CardInfo(
lastFourDigits = card.cardNumberEnd,
balance = fiatBalance.availableBalance,
currencyCode = fiatBalance.currency,
depositAddress = value.depositAddress,
isPinSet = value.card?.isPinSet == true,
fiatBalance = fiatBalance.toDomain(),
cryptoBalance = cryptoBalance.toDomain(),
)
} else {
null
}
val productInstance = value.productInstance?.let { instance ->
val status = instance.status.toDomain()
val cardFrozenState = when (status) {
Status.ACTIVE -> TangemPayCardFrozenState.Unfrozen
else -> TangemPayCardFrozenState.Frozen
}
val displayName = instance.displayName?.ifEmpty { null }
ProductInstance(
id = instance.id,
cardId = instance.cardId,
frozenState = cardFrozenState,
status = status,
displayName = if (displayName != null) CardDisplayName(displayName).getOrElse { null } else null,
actualCardLimit = instance.actualCardLimit?.parseCardLimit(),
adminCardLimit = instance.adminCardLimit?.parseCardLimit(),
)
}
return CustomerInfo(
customerId = value.id,
productInstance = productInstance,
kycStatus = kycStatus,
cardInfo = cardInfo,
state = CustomerInfo.State.fromString(value.state),
fiatBalance = fiatBalance?.toDomain(),
cryptoBalance = cryptoBalance?.toDomain(),
)
}
private fun CustomerMeResponse.CardLimit.parseCardLimit(): TangemPayCardLimit {
return TangemPayCardLimit(
amount = amount,
period = TangemPayCardLimitPeriod.fromString(periodType),
)
}
private fun FiatBalance.toDomain() = PaymentAccountStatusValue.FiatBalance(
availableBalance = availableBalance,
currency = currency,
)
private fun CryptoBalance.toDomain() = PaymentAccountStatusValue.CryptoBalance(
id = id,
chainId = chainId.toLong(),
depositAddress = depositAddress.orEmpty(),
tokenContractAddress = tokenContractAddress,
balance = balance,
)
private fun CustomerMeResponse.ProductInstance.Status.toDomain(): Status = when (this) {
CustomerMeResponse.ProductInstance.Status.NEW -> Status.NEW
CustomerMeResponse.ProductInstance.Status.READY_FOR_MANUFACTURING -> Status.READY_FOR_MANUFACTURING
CustomerMeResponse.ProductInstance.Status.MANUFACTURING -> Status.MANUFACTURING
CustomerMeResponse.ProductInstance.Status.SENT_TO_DELIVERY -> Status.SENT_TO_DELIVERY
CustomerMeResponse.ProductInstance.Status.DELIVERED -> Status.DELIVERED
CustomerMeResponse.ProductInstance.Status.ACTIVATING -> Status.ACTIVATING
CustomerMeResponse.ProductInstance.Status.ACTIVE -> Status.ACTIVE
CustomerMeResponse.ProductInstance.Status.BLOCKED -> Status.BLOCKED
CustomerMeResponse.ProductInstance.Status.DEACTIVATING -> Status.DEACTIVATING
CustomerMeResponse.ProductInstance.Status.DEACTIVATED -> Status.DEACTIVATED
CustomerMeResponse.ProductInstance.Status.CANCELED -> Status.CANCELED
CustomerMeResponse.ProductInstance.Status.UNKNOWN -> Status.UNKNOWN
}
}

View file

@ -5,7 +5,9 @@ import com.tangem.data.pay.entity.TangemPayCurrencyFactory
import com.tangem.datasource.local.visa.entity.PaymentAccountStatusValueDM
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
@ -17,9 +19,30 @@ internal class PaymentAccountStatusValueDMConverterTest {
private val tangemPayCurrencyFactory: TangemPayCurrencyFactory = mockk()
private val userWalletId = UserWalletId("1234567890ABCDEF")
private val cryptoCurrency: CryptoCurrency.Token = mockk()
init {
every { tangemPayCurrencyFactory.create(userWalletId) } returns cryptoCurrency
}
private val converter = PaymentAccountStatusValueDMConverter(tangemPayCurrencyFactory)
private fun cryptoBalance() = PaymentAccountStatusValue.CryptoBalance(
id = "usd-coin",
chainId = 137,
depositAddress = "0xDEPOSIT",
tokenContractAddress = "0xCONTRACT",
balance = BigDecimal("10"),
)
private fun cryptoBalanceDM() = PaymentAccountStatusValueDM.CryptoBalanceDM(
id = "usd-coin",
chainId = 137,
depositAddress = "0xDEPOSIT",
tokenContractAddress = "0xCONTRACT",
balance = BigDecimal("10"),
)
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class Convert {
@ -44,7 +67,9 @@ internal class PaymentAccountStatusValueDMConverterTest {
fiatBalance = PaymentAccountStatusValue.FiatBalance(
availableBalance = BigDecimal("100"),
currency = "USD",
)
),
cryptoBalance = cryptoBalance(),
cryptoCurrency = cryptoCurrency,
)
// WHEN
@ -117,7 +142,8 @@ internal class PaymentAccountStatusValueDMConverterTest {
fiatBalance = PaymentAccountStatusValueDM.FiatBalanceDM(
availableBalance = BigDecimal("200"),
currency = "EUR",
)
),
cryptoBalance = cryptoBalanceDM(),
)
// WHEN

View file

@ -104,7 +104,30 @@ sealed class PaymentAccountStatusValue {
data class Deactivated(
override val source: StatusSource,
val fiatBalance: FiatBalance,
) : PaymentAccountStatusValue()
val cryptoBalance: CryptoBalance,
val cryptoCurrency: CryptoCurrency.Token,
) : PaymentAccountStatusValue() {
val cryptoCurrencyStatus: CryptoCurrencyStatus = CryptoCurrencyStatus(
currency = cryptoCurrency,
value = CryptoCurrencyStatus.Loaded(
amount = cryptoBalance.balance,
fiatAmount = fiatBalance.availableBalance,
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ZERO,
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(
type = NetworkAddress.Address.Type.Primary,
value = cryptoBalance.depositAddress,
),
),
sources = CryptoCurrencyStatus.Sources(),
pendingTransactions = emptySet(),
stakingBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
),
)
}
/**
* Represents a state where the payment account is successfully loaded with complete information.

View file

@ -1,17 +0,0 @@
package com.tangem.domain.pay
import com.tangem.domain.models.account.CardDisplayName
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import kotlinx.serialization.Serializable
@Serializable
data class TangemPayDetailsConfig(
val customerId: String,
val cardId: String,
val isPinSet: Boolean,
val cardFrozenState: TangemPayCardFrozenState,
val cardNumberEnd: String,
val chainId: Int,
val isTangemPayDeactivated: Boolean,
val displayName: CardDisplayName?,
)

View file

@ -1,9 +1,9 @@
package com.tangem.domain.pay.model
import com.tangem.domain.models.pay.TangemPayCardLimit
import com.tangem.domain.models.account.CardDisplayName
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.kyc.KycStatus
import com.tangem.domain.models.pay.TangemPayCardLimit
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import java.math.BigDecimal
import java.util.Locale
@ -27,6 +27,7 @@ data class CustomerInfo(
val cardInfo: CardInfo?,
val state: State,
val fiatBalance: PaymentAccountStatusValue.FiatBalance?,
val cryptoBalance: PaymentAccountStatusValue.CryptoBalance?,
) {
enum class State {
NEW,

View file

@ -4,6 +4,7 @@ import arrow.core.Either
import com.tangem.core.error.UniversalError
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayWithdrawExchangeState
import com.tangem.domain.pay.WithdrawalResult
import java.math.BigDecimal
@ -18,7 +19,7 @@ interface TangemPayWithdrawRepository {
exchangeData: TangemPayWithdrawExchangeState,
): Either<UniversalError, WithdrawalResult>
suspend fun hasWithdrawOrder(userWallet: UserWallet): Boolean
suspend fun hasWithdrawOrder(userWalletId: UserWalletId): Boolean
suspend fun pollWithdrawOrdersIfNeeds(userWallet: UserWallet)
}

View file

@ -187,8 +187,8 @@ internal class ChooseTokenListItemConverter(
is PaymentAccountStatusValue.UnderReview,
PaymentAccountStatusValue.Loading,
PaymentAccountStatusValue.Empty,
is PaymentAccountStatusValue.Deactivated,
-> return null
is PaymentAccountStatusValue.Deactivated -> status.cryptoCurrencyStatus
is PaymentAccountStatusValue.Loaded -> status.cryptoCurrencyStatus
}
val account = this.account

View file

@ -130,6 +130,7 @@ internal class InitialCurrenciesResolver @Inject constructor(
private fun getPaymentAccountCurrencies(accountStatus: AccountStatus.Payment): List<CryptoCurrencyStatus> {
val paymentCryptoCurrencyStatus = when (val statusValue = accountStatus.value) {
is PaymentAccountStatusValue.Loaded -> statusValue.cryptoCurrencyStatus
is PaymentAccountStatusValue.Deactivated -> statusValue.cryptoCurrencyStatus
else -> null
}

View file

@ -2,10 +2,9 @@ package com.tangem.features.tangempay.components
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.models.account.AccountStatus
interface TangemPayDetailsContainerComponent : ComposableContentComponent {
data class Params(val userWalletId: UserWalletId, val config: TangemPayDetailsConfig)
data class Params(val initialStatus: AccountStatus.Payment)
interface Factory : ComponentFactory<Params, TangemPayDetailsContainerComponent>
}

View file

@ -67,7 +67,7 @@ internal class DefaultTangemPayDetailsContainerComponent @AssistedInject constru
)
TangemPayAccountDetailsInnerRoute.CardDetails -> tangemPayCardPageFactory.create(
context = childByContext(componentContext = componentContext, router = innerRouter),
params = TangemPayCardPageComponent.Params(userWalletId = params.userWalletId, config = params.config),
params = TangemPayCardPageComponent.Params(initialStatus = params.initialStatus),
)
}

View file

@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.TangemPayTopUpData
import com.tangem.features.tangempay.model.TangemPayAddFundsModel
@ -32,7 +33,7 @@ internal class TangemPayAddFundsComponent(
val cryptoBalance: BigDecimal,
val fiatBalance: BigDecimal,
val depositAddress: String,
val chainId: Int,
val cryptoCurrency: CryptoCurrency,
)
}

View file

@ -13,6 +13,8 @@ import com.tangem.features.tangempay.components.cardDetails.DefaultTangemPayCard
import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetailsBlockComponent
import com.tangem.features.tangempay.model.TangemPayAddToWalletModel
import com.tangem.features.tangempay.ui.TangemPayAddToWalletScreen
import com.tangem.features.tangempay.utils.firstCard
import com.tangem.features.tangempay.utils.userWalletId
internal class TangemPayAddToWalletComponent(
private val appComponentContext: AppComponentContext,
@ -24,7 +26,8 @@ internal class TangemPayAddToWalletComponent(
private val cardDetailsBlockComponent = DefaultTangemPayCardDetailsBlockComponent(
appComponentContext = child("cardDetailsBlockComponent"),
params = TangemPayCardDetailsBlockComponent.Params(
params = params,
card = params.initialStatus.firstCard(),
userWalletId = params.initialStatus.userWalletId,
isDisplayCardNameEnabled = false,
),
)

View file

@ -16,8 +16,7 @@ import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.decompose.navigation.inner.InnerRouter
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.models.account.AccountStatus
import com.tangem.features.tangempay.limit.setup.TangemPayCardLimitSetupComponent
import com.tangem.features.tangempay.limit.setup.TangemPayCardLimitSetupSuccessComponent
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
@ -70,34 +69,22 @@ internal class TangemPayCardPageComponent @AssistedInject constructor(
)
TangemPayCardDetailsInnerRoute.ChangePIN -> TangemPayChangePinComponent(
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
params = TangemPayDetailsContainerComponent.Params(
userWalletId = params.userWalletId,
config = params.config,
),
params = TangemPayDetailsContainerComponent.Params(initialStatus = params.initialStatus),
)
TangemPayCardDetailsInnerRoute.ChangePINSuccess -> TangemPayChangePinSuccessComponent(
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
)
TangemPayCardDetailsInnerRoute.AddToWallet -> TangemPayAddToWalletComponent(
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
params = TangemPayDetailsContainerComponent.Params(
userWalletId = params.userWalletId,
config = params.config,
),
params = TangemPayDetailsContainerComponent.Params(initialStatus = params.initialStatus),
)
TangemPayCardDetailsInnerRoute.EditCardDisplayName -> TangemPayEditDisplayNameComponent(
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
params = TangemPayDetailsContainerComponent.Params(
userWalletId = params.userWalletId,
config = params.config,
),
params = TangemPayDetailsContainerComponent.Params(initialStatus = params.initialStatus),
)
TangemPayCardDetailsInnerRoute.LimitSetup -> TangemPayCardLimitSetupComponent(
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
params = TangemPayDetailsContainerComponent.Params(
userWalletId = params.userWalletId,
config = params.config,
),
params = TangemPayDetailsContainerComponent.Params(initialStatus = params.initialStatus),
)
TangemPayCardDetailsInnerRoute.LimitSetupSuccess -> TangemPayCardLimitSetupSuccessComponent(
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
@ -112,7 +99,7 @@ internal class TangemPayCardPageComponent @AssistedInject constructor(
}
}
data class Params(val userWalletId: UserWalletId, val config: TangemPayDetailsConfig)
data class Params(val initialStatus: AccountStatus.Payment)
@AssistedFactory
interface Factory : ComponentFactory<Params, TangemPayCardPageComponent> {

View file

@ -20,6 +20,8 @@ import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetails
import com.tangem.features.tangempay.entity.TangemPayCardNavigation
import com.tangem.features.tangempay.model.TangemPayCardPageModel
import com.tangem.features.tangempay.ui.TangemPayCardPageScreen
import com.tangem.features.tangempay.utils.firstCard
import com.tangem.features.tangempay.utils.userWalletId
import com.tangem.features.tokenreceive.TokenReceiveComponent
internal class TangemPayCardPageScreenComponent(
@ -30,15 +32,11 @@ internal class TangemPayCardPageScreenComponent(
private val model: TangemPayCardPageModel = getOrCreateModel(params = params)
private val containerParams = TangemPayDetailsContainerComponent.Params(
userWalletId = params.userWalletId,
config = params.config,
)
private val cardDetailsBlockComponent = DefaultTangemPayCardDetailsBlockComponent(
appComponentContext = child("cardDetailsBlockComponent"),
params = TangemPayCardDetailsBlockComponent.Params(
params = containerParams,
card = params.initialStatus.firstCard(),
userWalletId = params.initialStatus.userWalletId,
isDisplayCardNameEnabled = true,
),
)
@ -86,8 +84,8 @@ internal class TangemPayCardPageScreenComponent(
appComponentContext = context,
params = TangemPayReissueCardComponent.Params(
listener = model,
userWalletId = params.userWalletId,
cardId = params.config.cardId,
userWalletId = params.initialStatus.userWalletId,
cardId = params.initialStatus.firstCard().id,
),
)
is TangemPayCardNavigation.AddFunds -> TangemPayAddFundsComponent(
@ -98,7 +96,7 @@ internal class TangemPayCardPageScreenComponent(
cryptoBalance = navigation.cryptoBalance,
fiatBalance = navigation.fiatBalance,
depositAddress = navigation.depositAddress,
chainId = navigation.chainId,
cryptoCurrency = navigation.cryptoCurrency,
),
)
is TangemPayCardNavigation.Receive -> tokenReceiveComponentFactory.create(

View file

@ -22,6 +22,8 @@ import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryDeta
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
import com.tangem.features.tangempay.model.TangemPayDetailsModel
import com.tangem.features.tangempay.ui.TangemPayDetailsScreen
import com.tangem.features.tangempay.utils.requireLoaded
import com.tangem.features.tangempay.utils.userWalletId
import com.tangem.features.tokenreceive.TokenReceiveComponent
internal class TangemPayDetailsComponent(
@ -42,7 +44,7 @@ internal class TangemPayDetailsComponent(
private val txHistoryComponent = DefaultTangemPayTxHistoryComponent(
appComponentContext = child("txHistoryComponent"),
params = DefaultTangemPayTxHistoryComponent.Params(
userWalletId = params.userWalletId,
userWalletId = params.initialStatus.userWalletId,
uiActions = model,
),
)
@ -50,7 +52,7 @@ internal class TangemPayDetailsComponent(
private val expressTransactionsComponent by lazy {
expressTransactionsComponentProvider.create(
appComponentContext = child("expressTransactionsComponent"),
userWalletId = params.userWalletId,
userWalletId = params.initialStatus.userWalletId,
cryptoCurrency = model.cryptoCurrency,
)
}
@ -95,8 +97,8 @@ internal class TangemPayDetailsComponent(
params = TangemPayTxHistoryDetailsComponent.Params(
transaction = navigation.transaction,
isBalanceHidden = navigation.isBalanceHidden,
userWalletId = params.userWalletId,
customerId = params.config.customerId,
userWalletId = params.initialStatus.userWalletId,
customerId = params.initialStatus.requireLoaded().customerId,
onDismiss = model.bottomSheetNavigation::dismiss,
),
)
@ -107,7 +109,7 @@ internal class TangemPayDetailsComponent(
cryptoBalance = navigation.cryptoBalance,
fiatBalance = navigation.fiatBalance,
depositAddress = navigation.depositAddress,
chainId = navigation.chainId,
cryptoCurrency = navigation.cryptoCurrency,
listener = model,
),
)

View file

@ -14,6 +14,8 @@ import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetails
import com.tangem.features.tangempay.entity.DisplayNameState
import com.tangem.features.tangempay.model.TangemPayEditDisplayNameModel
import com.tangem.features.tangempay.ui.TangemPayEditDisplayNameScreen
import com.tangem.features.tangempay.utils.firstCard
import com.tangem.features.tangempay.utils.userWalletId
internal class TangemPayEditDisplayNameComponent(
private val appComponentContext: AppComponentContext,
@ -24,7 +26,11 @@ internal class TangemPayEditDisplayNameComponent(
private val cardDetailsBlockComponent = DefaultTangemPayCardDetailsBlockComponent(
appComponentContext = child("editDisplayNameCardDetails"),
params = TangemPayCardDetailsBlockComponent.Params(params = params, isDisplayCardNameEnabled = true),
params = TangemPayCardDetailsBlockComponent.Params(
card = params.initialStatus.firstCard(),
userWalletId = params.initialStatus.userWalletId,
isDisplayCardNameEnabled = true,
),
)
@Composable

View file

@ -3,7 +3,8 @@ package com.tangem.features.tangempay.components.cardDetails
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
import com.tangem.domain.models.pay.TangemPayCard
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM
import kotlinx.coroutines.flow.StateFlow
@ -15,7 +16,8 @@ internal interface TangemPayCardDetailsBlockComponent {
fun CardDetailsBlockContent(state: TangemPayCardDetailsUM, modifier: Modifier)
data class Params(
val params: TangemPayDetailsContainerComponent.Params,
val card: TangemPayCard,
val userWalletId: UserWalletId,
val isDisplayCardNameEnabled: Boolean,
)
}

View file

@ -1,6 +1,7 @@
package com.tangem.features.tangempay.entity
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.serialization.SerializedBigDecimal
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.serialization.Serializable
@ -22,7 +23,7 @@ internal sealed class TangemPayCardNavigation {
val cryptoBalance: SerializedBigDecimal,
val fiatBalance: SerializedBigDecimal,
val depositAddress: String,
val chainId: Int,
val cryptoCurrency: CryptoCurrency,
) : TangemPayCardNavigation()
@Serializable

View file

@ -1,6 +1,7 @@
package com.tangem.features.tangempay.entity
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.serialization.SerializedBigDecimal
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
@ -18,7 +19,7 @@ internal sealed class TangemPayDetailsNavigation {
val cryptoBalance: SerializedBigDecimal,
val fiatBalance: SerializedBigDecimal,
val depositAddress: String,
val chainId: Int,
val cryptoCurrency: CryptoCurrency,
) : TangemPayDetailsNavigation()
@Serializable

View file

@ -55,7 +55,7 @@ internal class TangemPayDetailsStateFactory(
),
),
onAddCardClick = intents::onAddCardClick,
),
).takeIf { !isTangemPayDeactivated },
),
isBalanceHidden = false,
addFundsEnabled = true,

View file

@ -53,23 +53,23 @@ internal sealed interface DisplayNameState {
internal sealed class TangemPayDetailsBalanceBlockState {
abstract val actionButtons: ImmutableList<ActionButtonConfig>
abstract val cardsBlockState: CardsBlockState
abstract val cardsBlockState: CardsBlockState?
data class Loading(
override val actionButtons: ImmutableList<ActionButtonConfig>,
override val cardsBlockState: CardsBlockState,
override val cardsBlockState: CardsBlockState?,
) : TangemPayDetailsBalanceBlockState()
data class Content(
override val actionButtons: ImmutableList<ActionButtonConfig>,
override val cardsBlockState: CardsBlockState,
override val cardsBlockState: CardsBlockState?,
val fiatBalance: String,
val isBalanceFlickering: Boolean,
) : TangemPayDetailsBalanceBlockState()
data class Error(
override val actionButtons: ImmutableList<ActionButtonConfig>,
override val cardsBlockState: CardsBlockState,
override val cardsBlockState: CardsBlockState?,
) : TangemPayDetailsBalanceBlockState()
data class CardsBlockState(val cards: ImmutableList<Card>, val onAddCardClick: () -> Unit)

View file

@ -23,6 +23,8 @@ import com.tangem.domain.pay.usecase.SetTangemPayCardLimitUseCase
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
import com.tangem.features.tangempay.utils.firstCard
import com.tangem.features.tangempay.utils.userWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
@ -44,6 +46,8 @@ internal class TangemPayCardLimitSetupModel @Inject constructor(
) : Model() {
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
private val cardId: String = params.initialStatus.firstCard().id
private val userWalletId = params.initialStatus.userWalletId
private var currentAdminLimit: BigDecimal? = null
val uiState: StateFlow<TangemPayCardLimitSetupUM>
@ -70,15 +74,15 @@ internal class TangemPayCardLimitSetupModel @Inject constructor(
}
private fun observeCardState() {
paymentAccountStatusSupplier.invoke(params.userWalletId)
paymentAccountStatusSupplier.invoke(userWalletId)
.map { it.value }
.filterIsInstance<PaymentAccountStatusValue.Loaded>()
.filter { status ->
status.source == StatusSource.ACTUAL && status.findCardWithId(params.config.cardId) != null
status.source == StatusSource.ACTUAL && status.findCardWithId(cardId) != null
}
.withIndex()
.onEach { (index, status) ->
val card = status.requireCardWithId(params.config.cardId)
val card = status.requireCardWithId(cardId)
val currentLimit = card.limit?.actualCardLimit
?.takeIf { it.period == TangemPayCardLimitPeriod.DAY }
@ -131,8 +135,8 @@ internal class TangemPayCardLimitSetupModel @Inject constructor(
modelScope.launch {
uiState.update { it.copy(isSubmitButtonLoading = true) }
setTangemPayCardLimitUseCase(
cardId = params.config.cardId,
userWalletId = params.userWalletId,
cardId = cardId,
userWalletId = userWalletId,
amount = amount,
).fold(
ifLeft = {

View file

@ -6,9 +6,7 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.models.ReceiveAddressModel
import com.tangem.domain.models.ReceiveAddressModel.DisplayType
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
import com.tangem.domain.pay.model.TangemPayTopUpData
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.tangempay.components.TangemPayAddFundsComponent
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
import com.tangem.features.tangempay.model.transformers.TangemPayAddFundsUMConverter
@ -20,8 +18,6 @@ import javax.inject.Inject
internal class TangemPayAddFundsModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val tangemPayCryptoCurrencyFactory: TangemPayCryptoCurrencyFactory,
private val getUserWalletUseCase: GetUserWalletUseCase,
) : Model() {
private val params = paramsContainer.require<TangemPayAddFundsComponent.Params>()
@ -29,25 +25,19 @@ internal class TangemPayAddFundsModel @Inject constructor(
val uiState: TangemPayAddFundsUM = getInitialState()
private fun getInitialState(): TangemPayAddFundsUM {
val userWallet = getUserWalletUseCase(params.walletId).getOrNull()
val currency = userWallet?.let {
tangemPayCryptoCurrencyFactory.create(userWallet = userWallet, chainId = params.chainId).getOrNull()
}
val data = currency?.let {
TangemPayTopUpData(
currency = currency,
walletId = params.walletId,
cryptoBalance = params.cryptoBalance,
fiatBalance = params.fiatBalance,
depositAddress = params.depositAddress,
receiveAddress = listOf(
ReceiveAddressModel(
displayType = DisplayType.Default,
value = params.depositAddress,
),
val data = TangemPayTopUpData(
currency = params.cryptoCurrency,
walletId = params.walletId,
cryptoBalance = params.cryptoBalance,
fiatBalance = params.fiatBalance,
depositAddress = params.depositAddress,
receiveAddress = listOf(
ReceiveAddressModel(
displayType = DisplayType.Default,
value = params.depositAddress,
),
)
}
),
)
return TangemPayAddFundsUMConverter(listener = params.listener).convert(data)
}

View file

@ -49,16 +49,15 @@ internal class TangemPayCardDetailsBlockModel @Inject constructor(
) : Model() {
private val params: TangemPayCardDetailsBlockComponent.Params = paramsContainer.require()
private val card = params.card
private val stateFactory = TangemPayCardDetailsBlockStateFactory(
cardNumberEnd = params.params.config.cardNumberEnd,
displayNameState = if (params.isDisplayCardNameEnabled && params.params.config.displayName != null) {
cardNumberEnd = card.lastDigits,
displayNameState = card.displayName?.takeIf { params.isDisplayCardNameEnabled }?.let { displayName ->
DisplayNameState.Display(
displayName = requireNotNull(params.params.config.displayName).value,
displayName = displayName.value,
onClick = ::startEditingDisplayName,
)
} else {
null
},
onReveal = ::revealCardDetails,
onCopy = ::copyData,
@ -84,7 +83,7 @@ internal class TangemPayCardDetailsBlockModel @Inject constructor(
private fun subscribeToCardFrozenState() {
cardDetailsRepository
.cardFrozenState(params.params.config.cardId)
.cardFrozenState(card.id)
.onEach { uiState.update { state -> state.copy(cardFrozenState = it) } }
.launchIn(modelScope)
}
@ -95,7 +94,7 @@ internal class TangemPayCardDetailsBlockModel @Inject constructor(
uiState.transformerUpdate(
transformer = DetailsRevealProgressStateTransformer(onClickHide = ::hideCardDetails),
)
cardDetailsRepository.revealCardDetails(params.params.userWalletId)
cardDetailsRepository.revealCardDetails(params.userWalletId)
.onRight { cardDetails ->
uiState.transformerUpdate(
transformer = DetailsRevealedStateTransformer(

View file

@ -41,6 +41,9 @@ import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.*
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
import com.tangem.features.tangempay.utils.TangemPayMessagesFactory
import com.tangem.features.tangempay.utils.cryptoCurrency
import com.tangem.features.tangempay.utils.firstCard
import com.tangem.features.tangempay.utils.userWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
@ -65,6 +68,9 @@ internal class TangemPayCardPageModel @Inject constructor(
) : Model(), ViewPinListener, ReissueCardListener, AddFundsListener {
private val params: TangemPayCardPageComponent.Params = paramsContainer.require()
private val cardId: String = params.initialStatus.firstCard().id
private val userWalletId = params.initialStatus.userWalletId
private val cryptoCurrency = params.initialStatus.cryptoCurrency
private val addToWalletBannerJobHolder = JobHolder()
private val addFundsJobHolder = JobHolder()
@ -84,14 +90,14 @@ internal class TangemPayCardPageModel @Inject constructor(
init {
fetchAddToWalletBanner()
paymentAccountStatusSupplier.invoke(params.userWalletId)
paymentAccountStatusSupplier.invoke(userWalletId)
.onEach { state ->
val status = state.value
if (status is PaymentAccountStatusValue.Loaded &&
status.source == StatusSource.ACTUAL &&
status.hasCardWithId(params.config.cardId)
status.hasCardWithId(cardId)
) {
val card = status.requireCardWithId(params.config.cardId)
val card = status.requireCardWithId(cardId)
val limit = card.limit?.actualCardLimit?.takeIf { it.period == TangemPayCardLimitPeriod.DAY }
val dailyLimitState = if (limit != null) {
TangemPayDailyLimitBlockState.Content(
@ -137,8 +143,8 @@ internal class TangemPayCardPageModel @Inject constructor(
} else {
bottomSheetNavigation.activate(
TangemPayCardNavigation.ViewPinCode(
userWalletId = params.userWalletId,
cardId = params.config.cardId,
userWalletId = userWalletId,
cardId = cardId,
),
)
}
@ -163,7 +169,7 @@ internal class TangemPayCardPageModel @Inject constructor(
onReissueOrderStatusReceived(order.orderStatus)
if (order.orderStatus != OrderStatus.CANCELED) {
modelScope.launch {
reissueCardRepository.storeReissueOrderId(params.config.cardId, order.orderId)
reissueCardRepository.storeReissueOrderId(cardId, order.orderId)
}
} else {
uiMessageSender.send(SnackbarMessage(resourceReference(R.string.common_something_went_wrong)))
@ -177,7 +183,7 @@ internal class TangemPayCardPageModel @Inject constructor(
override fun onClickAddFunds() {
bottomSheetNavigation.dismiss()
modelScope.launch {
val balance = cardDetailsRepository.getCardBalance(params.userWalletId).getOrNull()
val balance = cardDetailsRepository.getCardBalance(userWalletId).getOrNull()
val depositAddress = balance?.depositAddress
if (balance == null || depositAddress == null) {
uiMessageSender.send(SnackbarMessage(resourceReference(R.string.common_error)))
@ -185,11 +191,11 @@ internal class TangemPayCardPageModel @Inject constructor(
}
bottomSheetNavigation.activate(
TangemPayCardNavigation.AddFunds(
walletId = params.userWalletId,
walletId = userWalletId,
fiatBalance = balance.fiatBalance,
cryptoBalance = balance.cryptoBalance,
depositAddress = depositAddress,
chainId = params.config.chainId,
cryptoCurrency = cryptoCurrency,
),
)
}.saveIn(addFundsJobHolder)
@ -232,8 +238,8 @@ internal class TangemPayCardPageModel @Inject constructor(
private fun freezeCard() {
modelScope.launch {
cardDetailsRepository.freezeCard(
userWalletId = params.userWalletId,
cardId = params.config.cardId,
userWalletId = userWalletId,
cardId = cardId,
).onLeft {
val message = SnackbarMessage(resourceReference(R.string.tangem_pay_freeze_card_failed))
uiMessageSender.send(message)
@ -251,8 +257,8 @@ internal class TangemPayCardPageModel @Inject constructor(
private fun unfreezeCard() {
modelScope.launch {
cardDetailsRepository.unfreezeCard(
userWalletId = params.userWalletId,
cardId = params.config.cardId,
userWalletId = userWalletId,
cardId = cardId,
).onLeft {
val message = SnackbarMessage(resourceReference(R.string.tangem_pay_unfreeze_card_failed))
uiMessageSender.send(message)
@ -269,7 +275,7 @@ internal class TangemPayCardPageModel @Inject constructor(
private fun fetchAddToWalletBanner() {
modelScope.launch {
val isDone = cardDetailsRepository.isAddToWalletDone(params.userWalletId).getOrNull() == true
val isDone = cardDetailsRepository.isAddToWalletDone(userWalletId).getOrNull() == true
if (!isDone) {
uiState.update { state ->
state.copy(
@ -289,7 +295,7 @@ internal class TangemPayCardPageModel @Inject constructor(
private fun onClickCloseBanner() {
modelScope.launch {
cardDetailsRepository.setAddToWalletAsDone(params.userWalletId)
cardDetailsRepository.setAddToWalletAsDone(userWalletId)
uiState.update { it.copy(addToWalletBlockState = null) }
}.saveIn(addToWalletBannerJobHolder)
}

View file

@ -17,6 +17,7 @@ import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayChangePinUM
import com.tangem.features.tangempay.model.transformers.PinCodeChangeTransformer
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
import com.tangem.features.tangempay.utils.userWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
import com.tangem.utils.transformer.update
@ -55,7 +56,7 @@ internal class TangemPayChangePinModel @Inject constructor(
uiState.update { it.copy(submitButtonLoading = true) }
val result = try {
cardDetailsRepository.setPin(
userWalletId = params.userWalletId,
userWalletId = params.initialStatus.userWalletId,
pin = uiState.value.pinCode,
).getOrNull()
} catch (e: Exception) {

View file

@ -20,16 +20,15 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.feedback.models.WalletMetaInfo
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
import com.tangem.domain.pay.model.TangemPayCardBalance
import com.tangem.domain.pay.model.TangemPayTopUpData
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.pay.repository.TangemPayWithdrawRepository
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.tangempay.TangemPayConstants
import com.tangem.features.tangempay.components.AddFundsListener
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
@ -44,10 +43,7 @@ import com.tangem.features.tangempay.model.transformers.DetailsBalanceTransforme
import com.tangem.features.tangempay.model.transformers.TangemPayDetailsRefreshTransformer
import com.tangem.features.tangempay.model.transformers.TangemPayFreezeUnfreezeStateTransformer
import com.tangem.features.tangempay.navigation.TangemPayAccountDetailsInnerRoute
import com.tangem.features.tangempay.utils.TangemPayDetailIntents
import com.tangem.features.tangempay.utils.TangemPayMessagesFactory
import com.tangem.features.tangempay.utils.TangemPayTxHistoryUiActions
import com.tangem.features.tangempay.utils.TangemPayTxHistoryUpdateListener
import com.tangem.features.tangempay.utils.*
import com.tangem.features.tokendetails.ExpressTransactionsEvent
import com.tangem.features.tokendetails.ExpressTransactionsEventListener
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -56,7 +52,10 @@ import com.tangem.utils.coroutines.saveIn
import com.tangem.utils.logging.TangemLogger
import com.tangem.utils.transformer.update
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -74,27 +73,38 @@ internal class TangemPayDetailsModel @Inject constructor(
private val uiMessageSender: UiMessageSender,
private val cardDetailsEventListener: CardDetailsEventListener,
private val txHistoryUpdateListener: TangemPayTxHistoryUpdateListener,
private val tangemPayCryptoCurrencyFactory: TangemPayCryptoCurrencyFactory,
private val tangemPayWithdrawRepository: TangemPayWithdrawRepository,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val expressTransactionsEventListener: ExpressTransactionsEventListener,
) : Model(), TangemPayTxHistoryUiActions, TangemPayDetailIntents, AddFundsListener {
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
private val userWalletId = params.initialStatus.userWalletId
private val isTangemPayDeactivated = params.initialStatus.isDeactivated
private val loaded: PaymentAccountStatusValue.Loaded? =
params.initialStatus.value as? PaymentAccountStatusValue.Loaded
private val firstCard = loaded?.cards?.firstOrNull()
val cryptoCurrency: CryptoCurrency = params.initialStatus.cryptoCurrency
private val initialCardFrozenState: TangemPayCardFrozenState = when {
firstCard == null -> TangemPayCardFrozenState.Unfrozen
firstCard.isFrozen -> TangemPayCardFrozenState.Frozen
else -> TangemPayCardFrozenState.Unfrozen
}
private val stateFactory = TangemPayDetailsStateFactory(
onBack = router::pop,
onOpenMenu = ::onOpenMenu,
intents = this,
cardFrozenState = params.config.cardFrozenState,
cardFrozenState = initialCardFrozenState,
)
val uiState: StateFlow<TangemPayDetailsUM>
field = MutableStateFlow(
stateFactory.getInitialState(
isTangemPayDeactivated = params.config.isTangemPayDeactivated,
cardNumberEnd = params.config.cardNumberEnd,
isTangemPayDeactivated = isTangemPayDeactivated,
cardNumberEnd = firstCard?.lastDigits.orEmpty(),
),
)
@ -103,19 +113,14 @@ internal class TangemPayDetailsModel @Inject constructor(
private var balance: TangemPayCardBalance? = null
private val userWallet: UserWallet? = getUserWalletUseCase(params.userWalletId).getOrNull()
val cryptoCurrency: CryptoCurrency? = userWallet?.let { wallet ->
tangemPayCryptoCurrencyFactory.create(userWallet = wallet, chainId = params.config.chainId).getOrNull()
}
val bottomSheetNavigation: SlotNavigation<TangemPayDetailsNavigation> = SlotNavigation()
init {
analytics.send(TangemPayAnalyticsEvents.MainScreenOpened())
handleBalanceHiding()
fetchBalance()
if (!params.config.isTangemPayDeactivated) {
subscribeToCardFrozenState()
if (!isTangemPayDeactivated && firstCard != null) {
subscribeToCardFrozenState(firstCard.id)
}
}
@ -131,9 +136,9 @@ internal class TangemPayDetailsModel @Inject constructor(
}
}
private fun subscribeToCardFrozenState() {
private fun subscribeToCardFrozenState(cardId: String) {
cardDetailsRepository
.cardFrozenState(params.config.cardId)
.cardFrozenState(cardId)
.onEach { uiState.update(TangemPayFreezeUnfreezeStateTransformer(cardFrozenState = it)) }
.launchIn(modelScope)
}
@ -147,11 +152,11 @@ internal class TangemPayDetailsModel @Inject constructor(
} else {
bottomSheetNavigation.activate(
TangemPayDetailsNavigation.AddFunds(
walletId = params.userWalletId,
walletId = userWalletId,
fiatBalance = currentBalance.availableForWithdrawal,
cryptoBalance = currentBalance.availableForWithdrawal,
depositAddress = depositAddress,
chainId = params.config.chainId,
cryptoCurrency = cryptoCurrency,
),
)
}
@ -163,31 +168,18 @@ internal class TangemPayDetailsModel @Inject constructor(
val depositAddress = currentBalance?.depositAddress
if (currentBalance == null || depositAddress == null) {
showBottomSheetError(TangemPayDetailsErrorType.Withdraw)
} else {
val userWallet = userWallet ?: getUserWalletUseCase(params.userWalletId).getOrNull()
if (userWallet == null) {
showBottomSheetError(TangemPayDetailsErrorType.Withdraw)
return
}
modelScope.launch {
val hasActiveWithdrawal = tangemPayWithdrawRepository.hasWithdrawOrder(userWalletId)
if (hasActiveWithdrawal) {
showBottomSheetError(TangemPayDetailsErrorType.WithdrawInProgress)
} else {
modelScope.launch {
val hasActiveWithdrawal = tangemPayWithdrawRepository.hasWithdrawOrder(userWallet = userWallet)
if (hasActiveWithdrawal) {
showBottomSheetError(TangemPayDetailsErrorType.WithdrawInProgress)
} else {
val currency = cryptoCurrency ?: tangemPayCryptoCurrencyFactory.create(
userWallet = userWallet,
chainId = params.config.chainId,
).getOrNull()
if (currency != null) {
uiMessageSender.send(
message = TangemPayMessagesFactory.createWithdrawWarning(
onGotItClick = { onConfirmWithdrawal(currency, currentBalance, depositAddress) },
),
)
} else {
showBottomSheetError(TangemPayDetailsErrorType.Withdraw)
}
}
}
uiMessageSender.send(
message = TangemPayMessagesFactory.createWithdrawWarning(
onGotItClick = { onConfirmWithdrawal(cryptoCurrency, currentBalance, depositAddress) },
),
)
}
}
}
@ -200,7 +192,7 @@ internal class TangemPayDetailsModel @Inject constructor(
router.push(
AppRoute.Swap(
cryptoCurrency = currency,
userWalletId = params.userWalletId,
userWalletId = userWalletId,
screenSource = AnalyticsParam.ScreensSources.TangemPay.value,
currencyPosition = AppRoute.Swap.CurrencyPosition.FROM,
tangemPayInput = AppRoute.Swap.TangemPayInput(
@ -216,18 +208,12 @@ internal class TangemPayDetailsModel @Inject constructor(
private fun fetchBalance(): Job {
return modelScope.launch {
val result = try {
cardDetailsRepository.getCardBalance(params.userWalletId).onRight { balance = it }
cardDetailsRepository.getCardBalance(userWalletId).onRight { balance = it }
} catch (e: Exception) {
TangemLogger.e("Error", e)
return@launch
}
uiState.update(
transformer = DetailsBalanceTransformer(
balance = result,
userWallet = getUserWalletUseCase(params.userWalletId).getOrNull(),
cryptoCurrencyFactory = tangemPayCryptoCurrencyFactory,
),
)
uiState.update(transformer = DetailsBalanceTransformer(balance = result))
}.saveIn(fetchBalanceJobHolder)
}
@ -239,11 +225,12 @@ internal class TangemPayDetailsModel @Inject constructor(
override fun onContactSupportClicked() {
analytics.send(Basic.ButtonSupport(source = AnalyticsParam.ScreensSources.TangemPay))
val customerId = loaded?.customerId ?: return
modelScope.launch {
sendFeedbackEmailUseCase.invoke(
type = FeedbackEmailType.Visa.FeatureIsBeta(
walletMetaInfo = WalletMetaInfo(userWalletId = params.userWalletId),
customerId = params.config.customerId,
walletMetaInfo = WalletMetaInfo(userWalletId = userWalletId),
customerId = customerId,
),
)
}

View file

@ -13,6 +13,8 @@ import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayEditDisplayNameUM
import com.tangem.features.tangempay.utils.firstCard
import com.tangem.features.tangempay.utils.userWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -32,7 +34,8 @@ internal class TangemPayEditDisplayNameModel @Inject constructor(
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
private val originalDisplayName = params.config.displayName?.value.orEmpty()
private val card = params.initialStatus.firstCard()
private val originalDisplayName = card.displayName?.value.orEmpty()
val uiState: StateFlow<TangemPayEditDisplayNameUM>
field = MutableStateFlow(
@ -62,8 +65,8 @@ internal class TangemPayEditDisplayNameModel @Inject constructor(
uiState.update { it.copy(isLoading = true) }
modelScope.launch {
cardDetailsRepository.updateCardDisplayName(
cardId = params.config.cardId,
userWalletId = params.userWalletId,
cardId = card.id,
userWalletId = params.initialStatus.userWalletId,
displayName = cardDisplayName,
).onRight {
router.pop()

View file

@ -4,8 +4,6 @@ import arrow.core.Either
import com.tangem.core.error.UniversalError
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
import com.tangem.domain.pay.model.TangemPayCardBalance
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
@ -15,8 +13,6 @@ import java.util.Currency
internal class DetailsBalanceTransformer(
private val balance: Either<UniversalError, TangemPayCardBalance>,
private val cryptoCurrencyFactory: TangemPayCryptoCurrencyFactory,
private val userWallet: UserWallet?,
) : Transformer<TangemPayDetailsUM> {
override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM {
@ -28,22 +24,12 @@ internal class DetailsBalanceTransformer(
)
}
is Either.Right<TangemPayCardBalance> -> {
val cryptoCurrency = userWallet?.let {
cryptoCurrencyFactory.create(userWallet, balance.value.chainId).getOrNull()
}
if (cryptoCurrency == null) {
TangemPayDetailsBalanceBlockState.Error(
actionButtons = persistentListOf(),
cardsBlockState = prevState.balanceBlockState.cardsBlockState,
)
} else {
TangemPayDetailsBalanceBlockState.Content(
isBalanceFlickering = false,
fiatBalance = getFiatBalanceText(balance.value),
actionButtons = prevState.balanceBlockState.actionButtons,
cardsBlockState = prevState.balanceBlockState.cardsBlockState,
)
}
TangemPayDetailsBalanceBlockState.Content(
isBalanceFlickering = false,
fiatBalance = getFiatBalanceText(balance.value),
actionButtons = prevState.balanceBlockState.actionButtons,
cardsBlockState = prevState.balanceBlockState.cardsBlockState,
)
}
}
return prevState.copy(balanceBlockState = balance)

View file

@ -229,12 +229,14 @@ private fun TangemPayDetailsBalanceBlock(
state = state,
isBalanceHidden = isBalanceHidden,
)
CardsBlockRow(
modifier = Modifier
.wrapContentSize()
.padding(horizontal = 12.dp, vertical = 8.dp),
cardsBlockState = state.cardsBlockState,
)
state.cardsBlockState?.let { cardsBlockState ->
CardsBlockRow(
modifier = Modifier
.wrapContentSize()
.padding(horizontal = 12.dp, vertical = 8.dp),
cardsBlockState = cardsBlockState,
)
}
if (state.actionButtons.isNotEmpty()) {
HorizontalActionChips(
modifier = Modifier.padding(top = 12.dp),

View file

@ -0,0 +1,26 @@
package com.tangem.features.tangempay.utils
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.pay.TangemPayCard
import com.tangem.domain.models.wallet.UserWalletId
internal val AccountStatus.Payment.userWalletId: UserWalletId
get() = account.userWalletId
internal val AccountStatus.Payment.cryptoCurrency: CryptoCurrency.Token
get() = when (val v = value) {
is PaymentAccountStatusValue.Loaded -> v.cryptoCurrency
is PaymentAccountStatusValue.Deactivated -> v.cryptoCurrency
else -> error("TangemPayDetails opened with unsupported status: $v")
}
internal val AccountStatus.Payment.isDeactivated: Boolean
get() = value is PaymentAccountStatusValue.Deactivated
internal fun AccountStatus.Payment.requireLoaded(): PaymentAccountStatusValue.Loaded =
value as? PaymentAccountStatusValue.Loaded
?: error("Card-detail subflow requires Loaded status, got ${value::class.simpleName}")
internal fun AccountStatus.Payment.firstCard(): TangemPayCard = requireLoaded().cards.first()

View file

@ -5,6 +5,7 @@ import com.tangem.core.decompose.model.MutableParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.pay.TangemPayCard
@ -12,10 +13,8 @@ import com.tangem.domain.models.pay.TangemPayCardLimit
import com.tangem.domain.models.pay.TangemPayCardLimitData
import com.tangem.domain.models.pay.TangemPayCardLimitPeriod
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
import com.tangem.domain.pay.usecase.SetTangemPayCardLimitUseCase
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.every
@ -40,20 +39,24 @@ internal class TangemPayCardLimitSetupModelTest {
private val setLimitUseCase: SetTangemPayCardLimitUseCase = mockk(relaxed = true)
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier = mockk()
private val params = TangemPayDetailsContainerComponent.Params(
userWalletId = userWalletId,
config = TangemPayDetailsConfig(
customerId = "customer1",
cardId = cardId,
isPinSet = false,
cardFrozenState = TangemPayCardFrozenState.Unfrozen,
cardNumberEnd = "1234",
chainId = 1,
isTangemPayDeactivated = false,
displayName = null,
),
private val initialCard = TangemPayCard(
id = cardId,
hasPinCode = false,
displayName = null,
isFrozen = false,
lastDigits = "1234",
limit = null,
)
private val initialStatus: AccountStatus.Payment = AccountStatus.Payment(
account = Account.Payment(userWalletId = userWalletId),
value = mockk<PaymentAccountStatusValue.Loaded>(relaxed = true) {
every { cards } returns listOf(initialCard)
},
)
private val params = TangemPayDetailsContainerComponent.Params(initialStatus = initialStatus)
private fun createModel(
adminLimit: BigDecimal? = BigDecimal("1000"),
): TangemPayCardLimitSetupModel {

View file

@ -18,9 +18,9 @@ import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.feedback.models.WalletMetaInfo
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
import com.tangem.domain.pay.model.TangemPayEntryPoint
@ -40,7 +40,7 @@ internal interface TangemPayIntents {
fun onRefreshPayToken(userWallet: UserWallet)
fun openDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
fun openDetails(status: AccountStatus.Payment)
fun onKycProgressClicked(userWalletId: UserWalletId)
@ -110,11 +110,8 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
}
}
override fun openDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
router.openTangemPayDetails(
userWalletId = userWalletId,
config = config,
)
override fun openDetails(status: AccountStatus.Payment) {
router.openTangemPayDetails(status = status)
}
override fun onKycProgressClicked(userWalletId: UserWalletId) {

View file

@ -12,13 +12,13 @@ import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.qrscanning.models.QrSendTarget
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.qrscanning.models.QrSendTarget
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.tokens.model.details.TokenAction
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent
@ -142,8 +142,8 @@ internal class DefaultWalletRouter @Inject constructor(
router.push(route = AppRoute.TangemPayOnboarding(mode = mode))
}
override fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
router.push(AppRoute.TangemPayDetails(userWalletId = userWalletId, config = config))
override fun openTangemPayDetails(status: AccountStatus.Payment) {
router.push(AppRoute.TangemPayDetails(status = status))
}
override fun openYieldSupplyBottomSheet(

View file

@ -7,13 +7,13 @@ import com.tangem.common.routing.AppRoute
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.qrscanning.models.QrSendTarget
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.qrscanning.models.QrSendTarget
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.tokens.model.details.TokenAction
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent
@ -82,7 +82,7 @@ internal interface InnerWalletRouter {
fun openTangemPayOnboarding(mode: AppRoute.TangemPayOnboarding.Mode)
fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
fun openTangemPayDetails(status: AccountStatus.Payment)
/** Open BS abput yield supply active and all money deposited in AAVE */
fun openYieldSupplyBottomSheet(

View file

@ -12,16 +12,12 @@ import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.kyc.KycStatus
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.feature.wallet.child.wallet.model.intents.TangemPayIntents
import com.tangem.features.tangempay.entity.TangemPayMainUM
import com.tangem.utils.converter.Converter
import java.math.BigDecimal
import java.util.Currency
private const val POLYGON_CHAIN_ID = 137
internal class TangemPayMainBlockConverter(
private val tangemPayClickIntents: TangemPayIntents,
private val isRedesignEnabled: Boolean,
@ -63,24 +59,9 @@ internal class TangemPayMainBlockConverter(
currencyCode = statusValue.fiatBalance.currency,
balance = statusValue.fiatBalance.availableBalance,
),
balanceSubtitle = stringReference("USDC"), // TODO hardcode for now
balanceSubtitle = stringReference(statusValue.cryptoCurrency.symbol),
shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE,
onClick = {
// Dummy config for deactivated account just to open details screen
tangemPayClickIntents.openDetails(
userWalletId = value.account.userWalletId,
config = TangemPayDetailsConfig(
customerId = "",
cardId = "",
isPinSet = false,
cardFrozenState = TangemPayCardFrozenState.Unfrozen,
cardNumberEnd = "",
chainId = POLYGON_CHAIN_ID,
displayName = null,
isTangemPayDeactivated = true,
),
)
},
onClick = { tangemPayClickIntents.openDetails(value) },
)
is PaymentAccountStatusValue.Loaded -> {
val card = statusValue.cards.firstOrNull() ?: return TangemPayMainUM.TemporaryUnavailable
@ -91,27 +72,9 @@ internal class TangemPayMainBlockConverter(
currencyCode = statusValue.currencyCode,
balance = statusValue.fiatBalance.availableBalance,
),
balanceSubtitle = stringReference("USDC"), // TODO hardcode for now
balanceSubtitle = stringReference(statusValue.cryptoCurrency.symbol),
shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE,
onClick = {
tangemPayClickIntents.openDetails(
value.account.userWalletId,
TangemPayDetailsConfig(
customerId = statusValue.customerId,
cardId = card.id,
isPinSet = card.hasPinCode,
cardFrozenState = if (card.isFrozen) {
TangemPayCardFrozenState.Frozen
} else {
TangemPayCardFrozenState.Unfrozen
},
cardNumberEnd = card.lastDigits,
chainId = POLYGON_CHAIN_ID,
displayName = card.displayName,
isTangemPayDeactivated = false,
),
)
},
onClick = { tangemPayClickIntents.openDetails(value) },
)
}
}