Updated on 2026-08-14
This commit is contained in:
parent
819fecffb6
commit
b3640235ef
15 changed files with 175 additions and 147 deletions
|
|
@ -40,6 +40,7 @@ sealed interface PaymentAccountStatusValueDM {
|
||||||
|
|
||||||
@NameLabel("active_account")
|
@NameLabel("active_account")
|
||||||
data class ActiveAccount(
|
data class ActiveAccount(
|
||||||
|
@Json(name = "active_account") val marker: Boolean = true,
|
||||||
@Json(name = "customer_id") val customerId: String,
|
@Json(name = "customer_id") val customerId: String,
|
||||||
@Json(name = "currency_code") val currencyCode: String,
|
@Json(name = "currency_code") val currencyCode: String,
|
||||||
@Json(name = "deposit_address") val depositAddress: String?,
|
@Json(name = "deposit_address") val depositAddress: String?,
|
||||||
|
|
@ -59,9 +60,11 @@ sealed interface PaymentAccountStatusValueDM {
|
||||||
@NameLabel("deactivated_account")
|
@NameLabel("deactivated_account")
|
||||||
data class DeactivatedAccount(
|
data class DeactivatedAccount(
|
||||||
@Json(name = "deactivated_account") val marker: Boolean = true,
|
@Json(name = "deactivated_account") val marker: Boolean = true,
|
||||||
|
@Json(name = "customer_id") val customerId: String,
|
||||||
@Json(name = "fiat_rate") val fiatRate: BigDecimal?,
|
@Json(name = "fiat_rate") val fiatRate: BigDecimal?,
|
||||||
@Json(name = "fiat_balance") val fiatBalance: FiatBalanceDM,
|
@Json(name = "fiat_balance") val fiatBalance: FiatBalanceDM,
|
||||||
@Json(name = "crypto_balance") val cryptoBalance: CryptoBalanceDM,
|
@Json(name = "crypto_balance") val cryptoBalance: CryptoBalanceDM,
|
||||||
|
@Json(name = "available_for_withdrawal") val availableForWithdrawal: BigDecimal,
|
||||||
) : PaymentAccountStatusValueDM
|
) : PaymentAccountStatusValueDM
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,11 @@ internal class PaymentAccountStatusValueDMConverter @Inject constructor(
|
||||||
is PaymentAccountStatusValue.IssuingCard -> PaymentAccountStatusValueDM.IssuingCard()
|
is PaymentAccountStatusValue.IssuingCard -> PaymentAccountStatusValueDM.IssuingCard()
|
||||||
is PaymentAccountStatusValue.Loaded -> PaymentAccountStatusValueDM.ActiveAccount(
|
is PaymentAccountStatusValue.Loaded -> PaymentAccountStatusValueDM.ActiveAccount(
|
||||||
customerId = value.customerId,
|
customerId = value.customerId,
|
||||||
currencyCode = value.currencyCode,
|
currencyCode = value.balance.fiatBalance.currency,
|
||||||
depositAddress = value.depositAddress,
|
depositAddress = value.depositAddress,
|
||||||
fiatBalance = value.fiatBalance.toDM(),
|
fiatBalance = value.balance.fiatBalance.toDM(),
|
||||||
cryptoBalance = value.cryptoBalance.toDM(),
|
cryptoBalance = value.balance.cryptoBalance.toDM(),
|
||||||
availableForWithdrawal = value.availableForWithdrawal,
|
availableForWithdrawal = value.balance.availableForWithdrawal,
|
||||||
fiatRate = value.fiatRate,
|
fiatRate = value.fiatRate,
|
||||||
cards = value.cards.map { card ->
|
cards = value.cards.map { card ->
|
||||||
PaymentAccountStatusValueDM.TangemPayCard(
|
PaymentAccountStatusValueDM.TangemPayCard(
|
||||||
|
|
@ -63,9 +63,11 @@ internal class PaymentAccountStatusValueDMConverter @Inject constructor(
|
||||||
)
|
)
|
||||||
is PaymentAccountStatusValue.Empty -> PaymentAccountStatusValueDM.Empty()
|
is PaymentAccountStatusValue.Empty -> PaymentAccountStatusValueDM.Empty()
|
||||||
is PaymentAccountStatusValue.Deactivated -> PaymentAccountStatusValueDM.DeactivatedAccount(
|
is PaymentAccountStatusValue.Deactivated -> PaymentAccountStatusValueDM.DeactivatedAccount(
|
||||||
|
customerId = value.customerId,
|
||||||
fiatRate = value.fiatRate,
|
fiatRate = value.fiatRate,
|
||||||
fiatBalance = value.fiatBalance.toDM(),
|
fiatBalance = value.balance.fiatBalance.toDM(),
|
||||||
cryptoBalance = value.cryptoBalance.toDM(),
|
cryptoBalance = value.balance.cryptoBalance.toDM(),
|
||||||
|
availableForWithdrawal = value.balance.availableForWithdrawal,
|
||||||
)
|
)
|
||||||
// Transient statuses are not persisted
|
// Transient statuses are not persisted
|
||||||
is PaymentAccountStatusValue.Loading,
|
is PaymentAccountStatusValue.Loading,
|
||||||
|
|
@ -90,11 +92,12 @@ internal class PaymentAccountStatusValueDMConverter @Inject constructor(
|
||||||
is PaymentAccountStatusValueDM.ActiveAccount -> PaymentAccountStatusValue.Loaded(
|
is PaymentAccountStatusValueDM.ActiveAccount -> PaymentAccountStatusValue.Loaded(
|
||||||
source = StatusSource.CACHE,
|
source = StatusSource.CACHE,
|
||||||
customerId = value.customerId,
|
customerId = value.customerId,
|
||||||
currencyCode = value.currencyCode,
|
|
||||||
depositAddress = value.depositAddress,
|
depositAddress = value.depositAddress,
|
||||||
fiatBalance = value.fiatBalance.toDomain(),
|
balance = PaymentAccountStatusValue.Balance(
|
||||||
cryptoBalance = value.cryptoBalance.toDomain(),
|
fiatBalance = value.fiatBalance.toDomain(),
|
||||||
availableForWithdrawal = value.availableForWithdrawal,
|
cryptoBalance = value.cryptoBalance.toDomain(),
|
||||||
|
availableForWithdrawal = value.availableForWithdrawal,
|
||||||
|
),
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
fiatRate = value.fiatRate,
|
fiatRate = value.fiatRate,
|
||||||
cards = value.cards.map { card ->
|
cards = value.cards.map { card ->
|
||||||
|
|
@ -123,8 +126,12 @@ internal class PaymentAccountStatusValueDMConverter @Inject constructor(
|
||||||
)
|
)
|
||||||
is PaymentAccountStatusValueDM.DeactivatedAccount -> PaymentAccountStatusValue.Deactivated(
|
is PaymentAccountStatusValueDM.DeactivatedAccount -> PaymentAccountStatusValue.Deactivated(
|
||||||
source = StatusSource.CACHE,
|
source = StatusSource.CACHE,
|
||||||
fiatBalance = value.fiatBalance.toDomain(),
|
customerId = value.customerId,
|
||||||
cryptoBalance = value.cryptoBalance.toDomain(),
|
balance = PaymentAccountStatusValue.Balance(
|
||||||
|
fiatBalance = value.fiatBalance.toDomain(),
|
||||||
|
cryptoBalance = value.cryptoBalance.toDomain(),
|
||||||
|
availableForWithdrawal = value.availableForWithdrawal,
|
||||||
|
),
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
fiatRate = value.fiatRate,
|
fiatRate = value.fiatRate,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import com.tangem.domain.pay.repository.TangemPayCloseCardRepository
|
||||||
import com.tangem.security.DeviceSecurityInfoProvider
|
import com.tangem.security.DeviceSecurityInfoProvider
|
||||||
import com.tangem.security.isSecurityExposed
|
import com.tangem.security.isSecurityExposed
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import com.tangem.utils.extensions.orZero
|
||||||
import com.tangem.utils.logging.TangemLogger
|
import com.tangem.utils.logging.TangemLogger
|
||||||
import kotlinx.coroutines.currentCoroutineContext
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
@ -287,11 +288,16 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
||||||
customerId = requireNotNull(customerId) { "CustomerId must not be null" },
|
customerId = requireNotNull(customerId) { "CustomerId must not be null" },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fiatBalance != null && cryptoBalance != null && (isDeactivated || isFormer) -> {
|
fiatBalance != null && cryptoBalance != null && !customerId.isNullOrEmpty() &&
|
||||||
|
(isDeactivated || isFormer) -> {
|
||||||
PaymentAccountStatusValue.Deactivated(
|
PaymentAccountStatusValue.Deactivated(
|
||||||
source = StatusSource.ACTUAL,
|
source = StatusSource.ACTUAL,
|
||||||
fiatBalance = fiatBalance,
|
customerId = requireNotNull(customerId) { "CustomerId must not be null" },
|
||||||
cryptoBalance = cryptoBalance,
|
balance = PaymentAccountStatusValue.Balance(
|
||||||
|
fiatBalance = fiatBalance,
|
||||||
|
cryptoBalance = cryptoBalance,
|
||||||
|
availableForWithdrawal = availableForWithdrawal.orZero(),
|
||||||
|
),
|
||||||
cryptoCurrency = tangemPayCurrencyFactory.create(userWalletId),
|
cryptoCurrency = tangemPayCurrencyFactory.create(userWalletId),
|
||||||
fiatRate = quotesData?.fiatRate,
|
fiatRate = quotesData?.fiatRate,
|
||||||
)
|
)
|
||||||
|
|
@ -321,11 +327,12 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
||||||
return PaymentAccountStatusValue.Loaded(
|
return PaymentAccountStatusValue.Loaded(
|
||||||
source = StatusSource.ACTUAL,
|
source = StatusSource.ACTUAL,
|
||||||
customerId = customerId,
|
customerId = customerId,
|
||||||
currencyCode = cardInfo.currencyCode,
|
|
||||||
depositAddress = cardInfo.depositAddress,
|
depositAddress = cardInfo.depositAddress,
|
||||||
fiatBalance = cardInfo.fiatBalance,
|
balance = PaymentAccountStatusValue.Balance(
|
||||||
cryptoBalance = cardInfo.cryptoBalance,
|
fiatBalance = cardInfo.fiatBalance,
|
||||||
availableForWithdrawal = cardInfo.availableForWithdrawal,
|
cryptoBalance = cardInfo.cryptoBalance,
|
||||||
|
availableForWithdrawal = cardInfo.availableForWithdrawal,
|
||||||
|
),
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
fiatRate = fiatRate,
|
fiatRate = fiatRate,
|
||||||
cards = listOf(
|
cards = listOf(
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ internal object CustomerInfoConverter : Converter<CustomerMeResponse.Result, Cus
|
||||||
state = CustomerInfo.State.fromString(value.state),
|
state = CustomerInfo.State.fromString(value.state),
|
||||||
fiatBalance = fiatBalance?.toDomain(),
|
fiatBalance = fiatBalance?.toDomain(),
|
||||||
cryptoBalance = cryptoBalance?.toDomain(),
|
cryptoBalance = cryptoBalance?.toDomain(),
|
||||||
|
availableForWithdrawal = value.balance?.availableForWithdrawal?.amount,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,15 @@ internal class PaymentAccountStatusValueDMConverterTest {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
val domain = PaymentAccountStatusValue.Deactivated(
|
val domain = PaymentAccountStatusValue.Deactivated(
|
||||||
source = StatusSource.ACTUAL,
|
source = StatusSource.ACTUAL,
|
||||||
fiatBalance = PaymentAccountStatusValue.FiatBalance(
|
customerId = "customer-1",
|
||||||
availableBalance = BigDecimal("100"),
|
balance = PaymentAccountStatusValue.Balance(
|
||||||
currency = "USD",
|
fiatBalance = PaymentAccountStatusValue.FiatBalance(
|
||||||
|
availableBalance = BigDecimal("100"),
|
||||||
|
currency = "USD",
|
||||||
|
),
|
||||||
|
cryptoBalance = cryptoBalance(),
|
||||||
|
availableForWithdrawal = BigDecimal("7"),
|
||||||
),
|
),
|
||||||
cryptoBalance = cryptoBalance(),
|
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
fiatRate = BigDecimal("1.05"),
|
fiatRate = BigDecimal("1.05"),
|
||||||
)
|
)
|
||||||
|
|
@ -79,8 +83,10 @@ internal class PaymentAccountStatusValueDMConverterTest {
|
||||||
// THEN
|
// THEN
|
||||||
assertThat(result).isInstanceOf(PaymentAccountStatusValueDM.DeactivatedAccount::class.java)
|
assertThat(result).isInstanceOf(PaymentAccountStatusValueDM.DeactivatedAccount::class.java)
|
||||||
val dm = result as PaymentAccountStatusValueDM.DeactivatedAccount
|
val dm = result as PaymentAccountStatusValueDM.DeactivatedAccount
|
||||||
|
assertThat(dm.customerId).isEqualTo("customer-1")
|
||||||
assertThat(dm.fiatBalance.availableBalance).isEqualTo(BigDecimal("100"))
|
assertThat(dm.fiatBalance.availableBalance).isEqualTo(BigDecimal("100"))
|
||||||
assertThat(dm.fiatBalance.currency).isEqualTo("USD")
|
assertThat(dm.fiatBalance.currency).isEqualTo("USD")
|
||||||
|
assertThat(dm.availableForWithdrawal).isEqualTo(BigDecimal("7"))
|
||||||
assertThat(dm.fiatRate).isEqualTo(BigDecimal("1.05"))
|
assertThat(dm.fiatRate).isEqualTo(BigDecimal("1.05"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,12 +147,14 @@ internal class PaymentAccountStatusValueDMConverterTest {
|
||||||
fun `GIVEN DM DeactivatedAccount WHEN convertBack THEN returns domain Deactivated with CACHE source`() {
|
fun `GIVEN DM DeactivatedAccount WHEN convertBack THEN returns domain Deactivated with CACHE source`() {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
val dm = PaymentAccountStatusValueDM.DeactivatedAccount(
|
val dm = PaymentAccountStatusValueDM.DeactivatedAccount(
|
||||||
|
customerId = "customer-2",
|
||||||
fiatBalance = PaymentAccountStatusValueDM.FiatBalanceDM(
|
fiatBalance = PaymentAccountStatusValueDM.FiatBalanceDM(
|
||||||
availableBalance = BigDecimal("200"),
|
availableBalance = BigDecimal("200"),
|
||||||
currency = "EUR",
|
currency = "EUR",
|
||||||
),
|
),
|
||||||
cryptoBalance = cryptoBalanceDM(),
|
cryptoBalance = cryptoBalanceDM(),
|
||||||
fiatRate = BigDecimal("0.92"),
|
fiatRate = BigDecimal("0.92"),
|
||||||
|
availableForWithdrawal = BigDecimal("5"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// WHEN
|
// WHEN
|
||||||
|
|
@ -156,8 +164,10 @@ internal class PaymentAccountStatusValueDMConverterTest {
|
||||||
assertThat(result).isInstanceOf(PaymentAccountStatusValue.Deactivated::class.java)
|
assertThat(result).isInstanceOf(PaymentAccountStatusValue.Deactivated::class.java)
|
||||||
val deactivated = result as PaymentAccountStatusValue.Deactivated
|
val deactivated = result as PaymentAccountStatusValue.Deactivated
|
||||||
assertThat(deactivated.source).isEqualTo(StatusSource.CACHE)
|
assertThat(deactivated.source).isEqualTo(StatusSource.CACHE)
|
||||||
assertThat(deactivated.fiatBalance.availableBalance).isEqualTo(BigDecimal("200"))
|
assertThat(deactivated.customerId).isEqualTo("customer-2")
|
||||||
assertThat(deactivated.fiatBalance.currency).isEqualTo("EUR")
|
assertThat(deactivated.balance.fiatBalance.availableBalance).isEqualTo(BigDecimal("200"))
|
||||||
|
assertThat(deactivated.balance.fiatBalance.currency).isEqualTo("EUR")
|
||||||
|
assertThat(deactivated.balance.availableForWithdrawal).isEqualTo(BigDecimal("5"))
|
||||||
assertThat(deactivated.fiatRate).isEqualTo(BigDecimal("0.92"))
|
assertThat(deactivated.fiatRate).isEqualTo(BigDecimal("0.92"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,11 @@ sealed class PaymentAccountStatusValue {
|
||||||
is Loading -> TotalFiatBalance.Loading
|
is Loading -> TotalFiatBalance.Loading
|
||||||
is Loaded -> {
|
is Loaded -> {
|
||||||
val rate = this.fiatRate ?: return TotalFiatBalance.Failed
|
val rate = this.fiatRate ?: return TotalFiatBalance.Failed
|
||||||
TotalFiatBalance.Loaded(amount = fiatBalance.availableBalance.multiply(rate), source = source)
|
TotalFiatBalance.Loaded(amount = balance.fiatBalance.availableBalance.multiply(rate), source = source)
|
||||||
}
|
}
|
||||||
is Deactivated -> {
|
is Deactivated -> {
|
||||||
val rate = this.fiatRate ?: return TotalFiatBalance.Failed
|
val rate = this.fiatRate ?: return TotalFiatBalance.Failed
|
||||||
TotalFiatBalance.Loaded(amount = fiatBalance.availableBalance.multiply(rate), source = source)
|
TotalFiatBalance.Loaded(amount = balance.fiatBalance.availableBalance.multiply(rate), source = source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,8 +104,8 @@ sealed class PaymentAccountStatusValue {
|
||||||
* Represents a state where the account is deactivated.
|
* Represents a state where the account is deactivated.
|
||||||
*
|
*
|
||||||
* @property source The source of the status information.
|
* @property source The source of the status information.
|
||||||
* @property fiatBalance The fiat balance details.
|
* @property customerId The unique identifier of the customer.
|
||||||
* @property cryptoBalance The crypto balance details.
|
* @property balance The balance details (fiat, crypto and amount available for withdrawal).
|
||||||
* @property cryptoCurrency The crypto currency held by the deactivated account.
|
* @property cryptoCurrency The crypto currency held by the deactivated account.
|
||||||
* @property fiatRate Exchange rate of [cryptoCurrency] to the account's fiat currency,
|
* @property fiatRate Exchange rate of [cryptoCurrency] to the account's fiat currency,
|
||||||
* or `null` if the quote is not yet available. When `null`,
|
* or `null` if the quote is not yet available. When `null`,
|
||||||
|
|
@ -114,18 +114,18 @@ sealed class PaymentAccountStatusValue {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Deactivated(
|
data class Deactivated(
|
||||||
override val source: StatusSource,
|
override val source: StatusSource,
|
||||||
val fiatBalance: FiatBalance,
|
val customerId: String,
|
||||||
val cryptoBalance: CryptoBalance,
|
val balance: Balance,
|
||||||
val cryptoCurrency: CryptoCurrency.Token,
|
val cryptoCurrency: CryptoCurrency.Token,
|
||||||
val fiatRate: SerializedBigDecimal?,
|
val fiatRate: SerializedBigDecimal?,
|
||||||
) : PaymentAccountStatusValue() {
|
) : PaymentAccountStatusValue() {
|
||||||
val cryptoCurrencyStatus: CryptoCurrencyStatus = CryptoCurrencyStatus(
|
val cryptoCurrencyStatus: CryptoCurrencyStatus = CryptoCurrencyStatus(
|
||||||
currency = cryptoCurrency,
|
currency = cryptoCurrency,
|
||||||
value = buildCryptoCurrencyStatusValue(
|
value = buildCryptoCurrencyStatusValue(
|
||||||
amount = cryptoBalance.balance,
|
amount = balance.cryptoBalance.balance,
|
||||||
fiatAmount = fiatBalance.availableBalance,
|
fiatAmount = balance.fiatBalance.availableBalance,
|
||||||
fiatRate = fiatRate,
|
fiatRate = fiatRate,
|
||||||
depositAddress = cryptoBalance.depositAddress,
|
depositAddress = balance.cryptoBalance.depositAddress,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -135,11 +135,9 @@ sealed class PaymentAccountStatusValue {
|
||||||
*
|
*
|
||||||
* @property source The source of the status information.
|
* @property source The source of the status information.
|
||||||
* @property customerId The unique identifier of the customer.
|
* @property customerId The unique identifier of the customer.
|
||||||
* @property currencyCode The code of the currency.
|
|
||||||
* @property depositAddress The address for deposits, if available.
|
* @property depositAddress The address for deposits, if available.
|
||||||
* @property fiatBalance The fiat balance details.
|
* @property balance The balance details (fiat, crypto and amount available for withdrawal).
|
||||||
* @property cryptoBalance The crypto balance details.
|
* The fiat currency code is available via [Balance.fiatBalance].
|
||||||
* @property availableForWithdrawal The crypto amount currently available for withdrawal/swap (excludes pending/locked funds).
|
|
||||||
* @property cryptoCurrency The crypto currency held by the account.
|
* @property cryptoCurrency The crypto currency held by the account.
|
||||||
* @property cards The list of user's cards.
|
* @property cards The list of user's cards.
|
||||||
* @property fiatRate Exchange rate of [cryptoCurrency] to the account's fiat currency,
|
* @property fiatRate Exchange rate of [cryptoCurrency] to the account's fiat currency,
|
||||||
|
|
@ -150,11 +148,8 @@ sealed class PaymentAccountStatusValue {
|
||||||
data class Loaded(
|
data class Loaded(
|
||||||
override val source: StatusSource,
|
override val source: StatusSource,
|
||||||
val customerId: String,
|
val customerId: String,
|
||||||
val currencyCode: String,
|
|
||||||
val depositAddress: String?,
|
val depositAddress: String?,
|
||||||
val fiatBalance: FiatBalance,
|
val balance: Balance,
|
||||||
val cryptoBalance: CryptoBalance,
|
|
||||||
val availableForWithdrawal: SerializedBigDecimal,
|
|
||||||
val cryptoCurrency: CryptoCurrency.Token,
|
val cryptoCurrency: CryptoCurrency.Token,
|
||||||
val cards: List<TangemPayCard>,
|
val cards: List<TangemPayCard>,
|
||||||
val fiatRate: SerializedBigDecimal?,
|
val fiatRate: SerializedBigDecimal?,
|
||||||
|
|
@ -162,10 +157,10 @@ sealed class PaymentAccountStatusValue {
|
||||||
val cryptoCurrencyStatus: CryptoCurrencyStatus = CryptoCurrencyStatus(
|
val cryptoCurrencyStatus: CryptoCurrencyStatus = CryptoCurrencyStatus(
|
||||||
currency = cryptoCurrency,
|
currency = cryptoCurrency,
|
||||||
value = buildCryptoCurrencyStatusValue(
|
value = buildCryptoCurrencyStatusValue(
|
||||||
amount = availableForWithdrawal,
|
amount = balance.availableForWithdrawal,
|
||||||
fiatAmount = fiatBalance.availableBalance,
|
fiatAmount = balance.fiatBalance.availableBalance,
|
||||||
fiatRate = fiatRate,
|
fiatRate = fiatRate,
|
||||||
depositAddress = cryptoBalance.depositAddress,
|
depositAddress = balance.cryptoBalance.depositAddress,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -202,6 +197,21 @@ sealed class PaymentAccountStatusValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aggregates all balance data of a payment account, as returned by the `customer/me` endpoint.
|
||||||
|
*
|
||||||
|
* @property fiatBalance The fiat balance details.
|
||||||
|
* @property cryptoBalance The crypto balance details.
|
||||||
|
* @property availableForWithdrawal The crypto amount currently available for withdrawal/swap
|
||||||
|
* (excludes pending/locked funds).
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class Balance(
|
||||||
|
val fiatBalance: FiatBalance,
|
||||||
|
val cryptoBalance: CryptoBalance,
|
||||||
|
val availableForWithdrawal: SerializedBigDecimal,
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the fiat balance of the payment account.
|
* Represents the fiat balance of the payment account.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ data class CustomerInfo(
|
||||||
val state: State,
|
val state: State,
|
||||||
val fiatBalance: PaymentAccountStatusValue.FiatBalance?,
|
val fiatBalance: PaymentAccountStatusValue.FiatBalance?,
|
||||||
val cryptoBalance: PaymentAccountStatusValue.CryptoBalance?,
|
val cryptoBalance: PaymentAccountStatusValue.CryptoBalance?,
|
||||||
|
val availableForWithdrawal: BigDecimal?,
|
||||||
) {
|
) {
|
||||||
enum class State {
|
enum class State {
|
||||||
NEW,
|
NEW,
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,7 @@ internal class SendDestinationModel @Inject constructor(
|
||||||
private fun AccountStatus.Payment.getDestinationWalletUM(wallet: UserWallet): DestinationWalletUM? {
|
private fun AccountStatus.Payment.getDestinationWalletUM(wallet: UserWallet): DestinationWalletUM? {
|
||||||
val contractAddress = (cryptoCurrency as? CryptoCurrency.Token)?.contractAddress ?: return null
|
val contractAddress = (cryptoCurrency as? CryptoCurrency.Token)?.contractAddress ?: return null
|
||||||
val (paymentAccountAddress, currency) = when (val status = this.value) {
|
val (paymentAccountAddress, currency) = when (val status = this.value) {
|
||||||
is PaymentAccountStatusValue.Loaded -> status.cryptoBalance.depositAddress to status.cryptoCurrency
|
is PaymentAccountStatusValue.Loaded -> status.balance.cryptoBalance.depositAddress to status.cryptoCurrency
|
||||||
else -> return null
|
else -> return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ internal class TangemPayCardLimitSetupModel @Inject constructor(
|
||||||
?.takeIf { it.period == TangemPayCardLimitPeriod.DAY }
|
?.takeIf { it.period == TangemPayCardLimitPeriod.DAY }
|
||||||
?.amount
|
?.amount
|
||||||
|
|
||||||
val currency = getJavaCurrencyByCode(status.currencyCode)
|
val currency = getJavaCurrencyByCode(status.balance.fiatBalance.currency)
|
||||||
uiState.update { state ->
|
uiState.update { state ->
|
||||||
val amount = if (index == 0) {
|
val amount = if (index == 0) {
|
||||||
currentLimit?.stripTrailingZeros()?.toPlainString().orEmpty()
|
currentLimit?.stripTrailingZeros()?.toPlainString().orEmpty()
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,9 @@ internal class TangemPayCardPageModel @Inject constructor(
|
||||||
val dailyLimitState = if (limit != null) {
|
val dailyLimitState = if (limit != null) {
|
||||||
TangemPayDailyLimitBlockState.Content(
|
TangemPayDailyLimitBlockState.Content(
|
||||||
limit = limit.amount.format {
|
limit = limit.amount.format {
|
||||||
val symbol = getJavaCurrencyByCode(status.currencyCode).symbol
|
val currencyCode = status.balance.fiatBalance.currency
|
||||||
fiat(status.currencyCode, symbol).optionalDecimals()
|
val symbol = getJavaCurrencyByCode(currencyCode).symbol
|
||||||
|
fiat(currencyCode, symbol).optionalDecimals()
|
||||||
},
|
},
|
||||||
onChangeClick = ::onClickLimitChange,
|
onChangeClick = ::onClickLimitChange,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.pay.TangemPayCardFrozenState
|
import com.tangem.domain.models.pay.TangemPayCardFrozenState
|
||||||
import com.tangem.domain.models.pay.TangemPayCardState
|
import com.tangem.domain.models.pay.TangemPayCardState
|
||||||
|
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||||
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
|
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
|
||||||
import com.tangem.domain.pay.model.TangemPayCardBalance
|
|
||||||
import com.tangem.domain.pay.model.TangemPayTopUpData
|
import com.tangem.domain.pay.model.TangemPayTopUpData
|
||||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||||
import com.tangem.domain.pay.repository.TangemPayWithdrawRepository
|
import com.tangem.domain.pay.repository.TangemPayWithdrawRepository
|
||||||
|
|
@ -40,8 +40,6 @@ import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
|
||||||
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
|
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
|
||||||
import com.tangem.features.tangempay.entity.TangemPayDetailsStateFactory
|
import com.tangem.features.tangempay.entity.TangemPayDetailsStateFactory
|
||||||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||||
import com.tangem.features.tangempay.model.listener.CardDetailsEvent
|
|
||||||
import com.tangem.features.tangempay.model.listener.CardDetailsEventListener
|
|
||||||
import com.tangem.features.tangempay.model.transformers.*
|
import com.tangem.features.tangempay.model.transformers.*
|
||||||
import com.tangem.features.tangempay.navigation.TangemPayAccountDetailsInnerRoute
|
import com.tangem.features.tangempay.navigation.TangemPayAccountDetailsInnerRoute
|
||||||
import com.tangem.features.tangempay.utils.*
|
import com.tangem.features.tangempay.utils.*
|
||||||
|
|
@ -52,7 +50,6 @@ import com.tangem.utils.coroutines.JobHolder
|
||||||
import com.tangem.utils.coroutines.saveIn
|
import com.tangem.utils.coroutines.saveIn
|
||||||
import com.tangem.utils.logging.TangemLogger
|
import com.tangem.utils.logging.TangemLogger
|
||||||
import com.tangem.utils.transformer.update
|
import com.tangem.utils.transformer.update
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -70,33 +67,36 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||||
private val uiMessageSender: UiMessageSender,
|
private val uiMessageSender: UiMessageSender,
|
||||||
private val cardDetailsEventListener: CardDetailsEventListener,
|
|
||||||
private val txHistoryUpdateListener: TangemPayTxHistoryUpdateListener,
|
private val txHistoryUpdateListener: TangemPayTxHistoryUpdateListener,
|
||||||
private val tangemPayWithdrawRepository: TangemPayWithdrawRepository,
|
private val tangemPayWithdrawRepository: TangemPayWithdrawRepository,
|
||||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||||
private val expressTransactionsEventListener: ExpressTransactionsEventListener,
|
private val expressTransactionsEventListener: ExpressTransactionsEventListener,
|
||||||
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
||||||
|
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
|
||||||
) : Model(), TangemPayTxHistoryUiActions, TangemPayDetailIntents, AddFundsListener {
|
) : Model(), TangemPayTxHistoryUiActions, TangemPayDetailIntents, AddFundsListener {
|
||||||
|
|
||||||
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
|
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
|
||||||
|
|
||||||
private val userWalletId = params.initialStatus.userWalletId
|
|
||||||
private val isTangemPayDeactivated = params.initialStatus.isDeactivated
|
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 {
|
private val initialCard = params.initialStatus.ifLoadedOrNull { it.cards.firstOrNull() }
|
||||||
firstCard == null -> TangemPayCardFrozenState.Unfrozen
|
|
||||||
else -> firstCard.frozenState
|
private val currentStatus = MutableStateFlow(params.initialStatus)
|
||||||
}
|
|
||||||
|
private val userWalletId
|
||||||
|
get() = currentStatus.value.userWalletId
|
||||||
|
|
||||||
|
val cryptoCurrency
|
||||||
|
get() = currentStatus.value.cryptoCurrency
|
||||||
|
|
||||||
private val stateFactory = TangemPayDetailsStateFactory(
|
private val stateFactory = TangemPayDetailsStateFactory(
|
||||||
onBack = router::pop,
|
onBack = router::pop,
|
||||||
onOpenMenu = ::onOpenMenu,
|
onOpenMenu = ::onOpenMenu,
|
||||||
intents = this,
|
intents = this,
|
||||||
cardFrozenState = initialCardFrozenState,
|
cardFrozenState = when {
|
||||||
|
initialCard == null -> TangemPayCardFrozenState.Unfrozen
|
||||||
|
else -> initialCard.frozenState
|
||||||
|
},
|
||||||
isRedesignEnabled = isRedesignEnabled(),
|
isRedesignEnabled = isRedesignEnabled(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -104,39 +104,49 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
field = MutableStateFlow(
|
field = MutableStateFlow(
|
||||||
stateFactory.getInitialState(
|
stateFactory.getInitialState(
|
||||||
isTangemPayDeactivated = isTangemPayDeactivated,
|
isTangemPayDeactivated = isTangemPayDeactivated,
|
||||||
cardNumberEnd = firstCard?.lastDigits.orEmpty(),
|
cardNumberEnd = initialCard?.lastDigits.orEmpty(),
|
||||||
isReissuing = firstCard == null || firstCard.state != TangemPayCardState.Active,
|
isReissuing = initialCard == null || initialCard.state != TangemPayCardState.Active,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val refreshStateJobHolder = JobHolder()
|
private val refreshStateJobHolder = JobHolder()
|
||||||
private val fetchBalanceJobHolder = JobHolder()
|
|
||||||
private val addToWalletBannerJobHolder = JobHolder()
|
private val addToWalletBannerJobHolder = JobHolder()
|
||||||
|
|
||||||
private var balance: TangemPayCardBalance? = null
|
|
||||||
|
|
||||||
val bottomSheetNavigation: SlotNavigation<TangemPayDetailsNavigation> = SlotNavigation()
|
val bottomSheetNavigation: SlotNavigation<TangemPayDetailsNavigation> = SlotNavigation()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
analytics.send(TangemPayAnalyticsEvents.MainScreenOpened())
|
analytics.send(TangemPayAnalyticsEvents.MainScreenOpened())
|
||||||
handleBalanceHiding()
|
handleBalanceHiding()
|
||||||
fetchBalance()
|
|
||||||
if (!isTangemPayDeactivated && firstCard != null) {
|
|
||||||
subscribeToCardFrozenState(firstCard.id)
|
|
||||||
fetchAddToWalletBanner()
|
|
||||||
|
|
||||||
paymentAccountStatusSupplier.invoke(userWalletId)
|
val statusFlow = paymentAccountStatusSupplier.invoke(userWalletId)
|
||||||
.map { it.value }
|
.onEach { status -> currentStatus.update { status } }
|
||||||
|
.map { it.value }
|
||||||
|
|
||||||
|
if (isTangemPayDeactivated) {
|
||||||
|
statusFlow
|
||||||
|
.filterIsInstance<PaymentAccountStatusValue.Deactivated>()
|
||||||
|
.onEach { state ->
|
||||||
|
uiState.update(DetailsBalanceTransformer(state.balance.fiatBalance))
|
||||||
|
}
|
||||||
|
.launchIn(modelScope)
|
||||||
|
} else {
|
||||||
|
if (initialCard != null) {
|
||||||
|
subscribeToCardFrozenState(initialCard.id)
|
||||||
|
}
|
||||||
|
fetchAddToWalletBanner()
|
||||||
|
statusFlow
|
||||||
.filterIsInstance<PaymentAccountStatusValue.Loaded>()
|
.filterIsInstance<PaymentAccountStatusValue.Loaded>()
|
||||||
.filter { it.source == StatusSource.ACTUAL }
|
.filter { it.source == StatusSource.ACTUAL }
|
||||||
.onEach { state ->
|
.onEach { state ->
|
||||||
val card = state.cards.firstOrNull() ?: return@onEach
|
uiState.update(DetailsBalanceTransformer(state.balance.fiatBalance))
|
||||||
uiState.update(
|
state.cards.firstOrNull()?.let { card ->
|
||||||
TangemPayCardDataTransformer(
|
uiState.update(
|
||||||
card = card,
|
TangemPayCardDataTransformer(
|
||||||
onCardClick = { onCardClick() },
|
card = card,
|
||||||
),
|
onCardClick = { onCardClick() },
|
||||||
)
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.launchIn(modelScope)
|
.launchIn(modelScope)
|
||||||
}
|
}
|
||||||
|
|
@ -165,17 +175,16 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
|
|
||||||
override fun onClickAddFunds() {
|
override fun onClickAddFunds() {
|
||||||
analytics.send(TangemPayAnalyticsEvents.AddFundsClicked())
|
analytics.send(TangemPayAnalyticsEvents.AddFundsClicked())
|
||||||
val currentBalance = balance
|
val balance = currentStatus.value.balanceOrNull()
|
||||||
val depositAddress = currentBalance?.depositAddress
|
if (balance == null) {
|
||||||
if (currentBalance == null || depositAddress == null) {
|
|
||||||
showBottomSheetError(TangemPayDetailsErrorType.Receive)
|
showBottomSheetError(TangemPayDetailsErrorType.Receive)
|
||||||
} else {
|
} else {
|
||||||
bottomSheetNavigation.activate(
|
bottomSheetNavigation.activate(
|
||||||
TangemPayDetailsNavigation.AddFunds(
|
TangemPayDetailsNavigation.AddFunds(
|
||||||
walletId = userWalletId,
|
walletId = userWalletId,
|
||||||
fiatBalance = currentBalance.availableForWithdrawal,
|
fiatBalance = balance.availableForWithdrawal,
|
||||||
cryptoBalance = currentBalance.availableForWithdrawal,
|
cryptoBalance = balance.availableForWithdrawal,
|
||||||
depositAddress = depositAddress,
|
depositAddress = balance.cryptoBalance.depositAddress,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -184,12 +193,6 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
|
|
||||||
override fun onClickWithdraw() {
|
override fun onClickWithdraw() {
|
||||||
analytics.send(TangemPayAnalyticsEvents.WithdrawClicked())
|
analytics.send(TangemPayAnalyticsEvents.WithdrawClicked())
|
||||||
val currentBalance = balance
|
|
||||||
val depositAddress = currentBalance?.depositAddress
|
|
||||||
if (currentBalance == null || depositAddress == null) {
|
|
||||||
showBottomSheetError(TangemPayDetailsErrorType.Withdraw)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
val hasActiveWithdrawal = tangemPayWithdrawRepository.hasWithdrawOrder(userWalletId)
|
val hasActiveWithdrawal = tangemPayWithdrawRepository.hasWithdrawOrder(userWalletId)
|
||||||
if (hasActiveWithdrawal) {
|
if (hasActiveWithdrawal) {
|
||||||
|
|
@ -197,18 +200,19 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
} else {
|
} else {
|
||||||
uiMessageSender.send(
|
uiMessageSender.send(
|
||||||
message = TangemPayMessagesFactory.createWithdrawWarning(
|
message = TangemPayMessagesFactory.createWithdrawWarning(
|
||||||
onGotItClick = { onConfirmWithdrawal(cryptoCurrency, currentBalance, depositAddress) },
|
onGotItClick = { onConfirmWithdrawal(cryptoCurrency) },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onConfirmWithdrawal(
|
private fun onConfirmWithdrawal(currency: CryptoCurrency) {
|
||||||
currency: CryptoCurrency,
|
val balance = currentStatus.value.balanceOrNull()
|
||||||
currentBalance: TangemPayCardBalance,
|
if (balance == null) {
|
||||||
depositAddress: String,
|
showBottomSheetError(TangemPayDetailsErrorType.Withdraw)
|
||||||
) {
|
return
|
||||||
|
}
|
||||||
router.push(
|
router.push(
|
||||||
AppRoute.Swap(
|
AppRoute.Swap(
|
||||||
cryptoCurrency = currency,
|
cryptoCurrency = currency,
|
||||||
|
|
@ -216,27 +220,15 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
screenSource = AnalyticsParam.ScreensSources.TangemPay.value,
|
screenSource = AnalyticsParam.ScreensSources.TangemPay.value,
|
||||||
currencyPosition = AppRoute.Swap.CurrencyPosition.FROM,
|
currencyPosition = AppRoute.Swap.CurrencyPosition.FROM,
|
||||||
tangemPayInput = AppRoute.Swap.TangemPayInput(
|
tangemPayInput = AppRoute.Swap.TangemPayInput(
|
||||||
cryptoAmount = currentBalance.availableForWithdrawal,
|
cryptoAmount = balance.availableForWithdrawal,
|
||||||
fiatAmount = currentBalance.availableForWithdrawal,
|
fiatAmount = balance.availableForWithdrawal,
|
||||||
depositAddress = depositAddress,
|
depositAddress = balance.cryptoBalance.depositAddress,
|
||||||
isWithdrawal = true,
|
isWithdrawal = true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchBalance(): Job {
|
|
||||||
return modelScope.launch {
|
|
||||||
val result = try {
|
|
||||||
cardDetailsRepository.getCardBalance(userWalletId).onRight { balance = it }
|
|
||||||
} catch (e: Exception) {
|
|
||||||
TangemLogger.e("Error", e)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
uiState.update(transformer = DetailsBalanceTransformer(balance = result))
|
|
||||||
}.saveIn(fetchBalanceJobHolder)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fetchAddToWalletBanner() {
|
private fun fetchAddToWalletBanner() {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
val isDone = try {
|
val isDone = try {
|
||||||
|
|
@ -263,7 +255,7 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
|
|
||||||
override fun onContactSupportClicked() {
|
override fun onContactSupportClicked() {
|
||||||
analytics.send(Basic.ButtonSupport(source = AnalyticsParam.ScreensSources.TangemPay))
|
analytics.send(Basic.ButtonSupport(source = AnalyticsParam.ScreensSources.TangemPay))
|
||||||
val customerId = loaded?.customerId ?: return
|
val customerId = currentStatus.value.ifLoadedOrNull { it.customerId } ?: return
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
sendFeedbackEmailUseCase.invoke(
|
sendFeedbackEmailUseCase.invoke(
|
||||||
type = FeedbackEmailType.Visa.FeatureIsBeta(
|
type = FeedbackEmailType.Visa.FeatureIsBeta(
|
||||||
|
|
@ -277,10 +269,9 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
override fun onRefreshSwipe(refreshState: ShowRefreshState) {
|
override fun onRefreshSwipe(refreshState: ShowRefreshState) {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
uiState.update(TangemPayDetailsRefreshTransformer(isRefreshing = refreshState.value))
|
uiState.update(TangemPayDetailsRefreshTransformer(isRefreshing = refreshState.value))
|
||||||
cardDetailsEventListener.send(CardDetailsEvent.Hide)
|
paymentAccountStatusFetcher.invoke(userWalletId)
|
||||||
expressTransactionsEventListener.send(ExpressTransactionsEvent.Update)
|
expressTransactionsEventListener.send(ExpressTransactionsEvent.Update)
|
||||||
txHistoryUpdateListener.triggerUpdate()
|
txHistoryUpdateListener.triggerUpdate()
|
||||||
fetchBalance().join()
|
|
||||||
uiState.update(TangemPayDetailsRefreshTransformer(isRefreshing = false))
|
uiState.update(TangemPayDetailsRefreshTransformer(isRefreshing = false))
|
||||||
}.saveIn(refreshStateJobHolder)
|
}.saveIn(refreshStateJobHolder)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,32 @@
|
||||||
package com.tangem.features.tangempay.model.transformers
|
package com.tangem.features.tangempay.model.transformers
|
||||||
|
|
||||||
import arrow.core.Either
|
|
||||||
import com.tangem.core.error.UniversalError
|
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||||
import com.tangem.core.ui.format.bigdecimal.formatStyled
|
import com.tangem.core.ui.format.bigdecimal.formatStyled
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.domain.pay.model.TangemPayCardBalance
|
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||||
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
|
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
|
||||||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||||
import com.tangem.utils.transformer.Transformer
|
import com.tangem.utils.transformer.Transformer
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
import java.util.Currency
|
import java.util.Currency
|
||||||
|
|
||||||
internal class DetailsBalanceTransformer(
|
internal class DetailsBalanceTransformer(
|
||||||
private val balance: Either<UniversalError, TangemPayCardBalance>,
|
private val fiatBalance: PaymentAccountStatusValue.FiatBalance,
|
||||||
) : Transformer<TangemPayDetailsUM> {
|
) : Transformer<TangemPayDetailsUM> {
|
||||||
|
|
||||||
override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM {
|
override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM {
|
||||||
val balance = when (balance) {
|
val balance = TangemPayDetailsBalanceBlockState.Content(
|
||||||
is Either.Left<UniversalError> -> {
|
isBalanceFlickering = false,
|
||||||
TangemPayDetailsBalanceBlockState.Error(
|
fiatBalance = getFiatBalanceText(fiatBalance),
|
||||||
actionButtons = persistentListOf(),
|
actionButtons = prevState.balanceBlockState.actionButtons,
|
||||||
cardsBlockState = prevState.balanceBlockState.cardsBlockState,
|
cardsBlockState = prevState.balanceBlockState.cardsBlockState,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
is Either.Right<TangemPayCardBalance> -> {
|
|
||||||
TangemPayDetailsBalanceBlockState.Content(
|
|
||||||
isBalanceFlickering = false,
|
|
||||||
fiatBalance = getFiatBalanceText(balance.value),
|
|
||||||
actionButtons = prevState.balanceBlockState.actionButtons,
|
|
||||||
cardsBlockState = prevState.balanceBlockState.cardsBlockState,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prevState.copy(balanceBlockState = balance)
|
return prevState.copy(balanceBlockState = balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFiatBalanceText(balance: TangemPayCardBalance): TextReference {
|
private fun getFiatBalanceText(fiatBalance: PaymentAccountStatusValue.FiatBalance): TextReference {
|
||||||
val currency = Currency.getInstance(balance.currencyCode)
|
val currency = Currency.getInstance(fiatBalance.currency)
|
||||||
return balance.fiatBalance.formatStyled {
|
return fiatBalance.availableBalance.formatStyled {
|
||||||
fiat(
|
fiat(
|
||||||
fiatCurrencyCode = currency.currencyCode,
|
fiatCurrencyCode = currency.currencyCode,
|
||||||
fiatCurrencySymbol = currency.symbol,
|
fiatCurrencySymbol = currency.symbol,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,12 @@ internal inline fun <T> AccountStatus.Payment.ifLoadedOrNull(call: (PaymentAccou
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun AccountStatus.Payment.balanceOrNull(): PaymentAccountStatusValue.Balance? = when (val v = value) {
|
||||||
|
is PaymentAccountStatusValue.Loaded -> v.balance
|
||||||
|
is PaymentAccountStatusValue.Deactivated -> v.balance
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
internal fun AccountStatus.Payment.findCard(
|
internal fun AccountStatus.Payment.findCard(
|
||||||
initialCardId: String,
|
initialCardId: String,
|
||||||
initialStatus: AccountStatus.Payment,
|
initialStatus: AccountStatus.Payment,
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,11 @@ internal class TangemPayCardLimitSetupModelTest {
|
||||||
val statusWithLimit: PaymentAccountStatusValue.Loaded = mockk(relaxed = true) {
|
val statusWithLimit: PaymentAccountStatusValue.Loaded = mockk(relaxed = true) {
|
||||||
every { source } returns StatusSource.ACTUAL
|
every { source } returns StatusSource.ACTUAL
|
||||||
every { cards } returns listOf(cardWithLimit)
|
every { cards } returns listOf(cardWithLimit)
|
||||||
every { currencyCode } returns "USD"
|
every { balance } returns mockk(relaxed = true) {
|
||||||
|
every { fiatBalance } returns mockk(relaxed = true) {
|
||||||
|
every { currency } returns "USD"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val paymentStatusWithLimit: AccountStatus.Payment = mockk(relaxed = true) {
|
val paymentStatusWithLimit: AccountStatus.Payment = mockk(relaxed = true) {
|
||||||
every { value } returns statusWithLimit
|
every { value } returns statusWithLimit
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ internal class TangemPayMainBlockConverter(
|
||||||
subtitle = TextReference.Res(R.string.tangempay_status_deactivated),
|
subtitle = TextReference.Res(R.string.tangempay_status_deactivated),
|
||||||
isBalanceFlickering = statusValue.source == StatusSource.CACHE,
|
isBalanceFlickering = statusValue.source == StatusSource.CACHE,
|
||||||
balance = getBalanceText(
|
balance = getBalanceText(
|
||||||
currencyCode = statusValue.fiatBalance.currency,
|
currencyCode = statusValue.balance.fiatBalance.currency,
|
||||||
balance = statusValue.fiatBalance.availableBalance,
|
balance = statusValue.balance.fiatBalance.availableBalance,
|
||||||
),
|
),
|
||||||
balanceSubtitle = stringReference(statusValue.cryptoCurrency.symbol),
|
balanceSubtitle = stringReference(statusValue.cryptoCurrency.symbol),
|
||||||
shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE,
|
shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE,
|
||||||
|
|
@ -75,8 +75,8 @@ internal class TangemPayMainBlockConverter(
|
||||||
},
|
},
|
||||||
isBalanceFlickering = statusValue.source == StatusSource.CACHE,
|
isBalanceFlickering = statusValue.source == StatusSource.CACHE,
|
||||||
balance = getBalanceText(
|
balance = getBalanceText(
|
||||||
currencyCode = statusValue.currencyCode,
|
currencyCode = statusValue.balance.fiatBalance.currency,
|
||||||
balance = statusValue.fiatBalance.availableBalance,
|
balance = statusValue.balance.fiatBalance.availableBalance,
|
||||||
),
|
),
|
||||||
balanceSubtitle = stringReference(statusValue.cryptoCurrency.symbol),
|
balanceSubtitle = stringReference(statusValue.cryptoCurrency.symbol),
|
||||||
shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE,
|
shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue