Updated on 2026-08-14
This commit is contained in:
parent
5618f44c8b
commit
084bad17ab
18 changed files with 452 additions and 192 deletions
|
|
@ -4,9 +4,11 @@ import com.tangem.datasource.local.datastore.RuntimeDataStore
|
|||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.visa.DefaultTangemPayCardFrozenStateStore
|
||||
import com.tangem.datasource.local.visa.DefaultTangemPayCloseCardStore
|
||||
import com.tangem.datasource.local.visa.DefaultTangemPayIssueCardStore
|
||||
import com.tangem.datasource.local.visa.DefaultTangemPayReissueCardStore
|
||||
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
|
||||
import com.tangem.datasource.local.visa.TangemPayCloseCardStore
|
||||
import com.tangem.datasource.local.visa.TangemPayIssueCardStore
|
||||
import com.tangem.datasource.local.visa.TangemPayReissueCardStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -42,4 +44,12 @@ internal object TangemPayStoresModule {
|
|||
prefs = prefs,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTangemPayIssueCardStore(prefs: AppPreferencesStore): TangemPayIssueCardStore {
|
||||
return DefaultTangemPayIssueCardStore(
|
||||
prefs = prefs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.datasource.local.visa
|
||||
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectListSync
|
||||
import com.tangem.datasource.local.preferences.utils.storeObjectList
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
internal class DefaultTangemPayIssueCardStore(
|
||||
private val prefs: AppPreferencesStore,
|
||||
) : TangemPayIssueCardStore {
|
||||
|
||||
override suspend fun addIssueOrderId(userWalletId: UserWalletId, orderId: String) {
|
||||
val current = prefs.getObjectListSync<String>(getKey(userWalletId))
|
||||
if (orderId !in current) {
|
||||
prefs.storeObjectList(key = getKey(userWalletId), value = current + orderId)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getIssueOrderIds(userWalletId: UserWalletId): List<String> {
|
||||
return prefs.getObjectListSync(getKey(userWalletId))
|
||||
}
|
||||
|
||||
override suspend fun removeIssueOrderId(userWalletId: UserWalletId, orderId: String) {
|
||||
val current = prefs.getObjectListSync<String>(getKey(userWalletId))
|
||||
if (orderId in current) {
|
||||
prefs.storeObjectList(key = getKey(userWalletId), value = current - orderId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getKey(userWalletId: UserWalletId) =
|
||||
stringPreferencesKey("tangem_pay_issue_card_orders_${userWalletId.stringValue}")
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.datasource.local.visa
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
/**
|
||||
* Local store for additional-card issuance order ids.
|
||||
*
|
||||
* The backend `customer/me` response does not surface a card while it is still being issued, so the
|
||||
* order id of an in-flight additional-card issuance is persisted here (per wallet) to render an
|
||||
* "issuing" placeholder card until the order reaches a terminal state and the real card appears.
|
||||
*/
|
||||
interface TangemPayIssueCardStore {
|
||||
|
||||
suspend fun addIssueOrderId(userWalletId: UserWalletId, orderId: String)
|
||||
|
||||
suspend fun getIssueOrderIds(userWalletId: UserWalletId): List<String>
|
||||
|
||||
suspend fun removeIssueOrderId(userWalletId: UserWalletId, orderId: String)
|
||||
}
|
||||
|
|
@ -75,6 +75,10 @@ internal interface TangemPayDataModule {
|
|||
@Singleton
|
||||
fun bindCloseCardRepository(repository: DefaultCloseCardRepository): TangemPayCloseCardRepository
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindIssueCardRepository(repository: DefaultIssueCardRepository): TangemPayIssueCardRepository
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindTangemPayCryptoCurrencyFactory(factory: DefaultTangemPayCurrencyFactory): TangemPayCurrencyFactory
|
||||
|
|
@ -269,8 +273,17 @@ internal interface TangemPayDataModule {
|
|||
fun provideIssueAdditionalCardUseCase(
|
||||
customerOffersRepository: CustomerOffersRepository,
|
||||
customerOrderRepository: CustomerOrderRepository,
|
||||
issueCardRepository: TangemPayIssueCardRepository,
|
||||
startTangemPayOrderPollingUseCase: StartTangemPayOrderPollingUseCase,
|
||||
appCoroutineScope: AppCoroutineScope,
|
||||
): IssueAdditionalCardUseCase {
|
||||
return IssueAdditionalCardUseCase(customerOffersRepository, customerOrderRepository)
|
||||
return IssueAdditionalCardUseCase(
|
||||
customerOffersRepository = customerOffersRepository,
|
||||
customerOrderRepository = customerOrderRepository,
|
||||
issueCardRepository = issueCardRepository,
|
||||
startTangemPayOrderPollingUseCase = startTangemPayOrderPollingUseCase,
|
||||
appCoroutineScope = appCoroutineScope,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -53,6 +53,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
private val singleQuoteSupplier: SingleQuoteStatusSupplier,
|
||||
private val closeCardRepository: TangemPayCloseCardRepository,
|
||||
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||
private val issueCardRepository: TangemPayIssueCardRepository,
|
||||
) : PaymentAccountStatusFetcher {
|
||||
|
||||
private val logger = TangemLogger.withTag(TAG)
|
||||
|
|
@ -360,13 +361,17 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
|
||||
if (tangemPayCards.isEmpty()) return PaymentAccountStatusValue.IssuingCard(source = StatusSource.ACTUAL)
|
||||
|
||||
// Additional-card issuance: the backend omits the new card until it is provisioned, so surface a
|
||||
// placeholder for every locally tracked in-flight issuance order alongside the real cards.
|
||||
val issuingCards = buildIssuingCards(userWalletId)
|
||||
|
||||
return PaymentAccountStatusValue.Loaded(
|
||||
source = StatusSource.ACTUAL,
|
||||
customerId = customerId,
|
||||
depositAddress = cryptoBalance.depositAddress,
|
||||
cryptoCurrency = tangemPayCurrencyFactory.create(userWalletId),
|
||||
fiatRate = fiatRate,
|
||||
cards = tangemPayCards,
|
||||
cards = tangemPayCards + issuingCards,
|
||||
balance = PaymentAccountStatusValue.Balance(
|
||||
fiatBalance = fiatBalance,
|
||||
cryptoBalance = cryptoBalance,
|
||||
|
|
@ -399,6 +404,37 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the issuing placeholder cards from locally tracked additional-card orders. Each order is
|
||||
* re-checked against the backend; terminal orders are dropped (and forgotten) because the real card
|
||||
* is now part of [CustomerInfo], while in-flight orders surface as an issuing placeholder card.
|
||||
*/
|
||||
private suspend fun buildIssuingCards(userWalletId: UserWalletId): List<TangemPayCard> {
|
||||
val orderIds = issueCardRepository.getIssueOrderIds(userWalletId)
|
||||
return orderIds.mapNotNull { orderId ->
|
||||
val order = cardDetailsRepository.getOrderInfo(userWalletId, orderId).getOrNull()
|
||||
if (order != null && order.orderStatus.isTerminal) {
|
||||
issueCardRepository.removeIssueOrderId(userWalletId, orderId)
|
||||
null
|
||||
} else {
|
||||
issuingPlaceholderCard(orderId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Placeholder card for an additional card that is still being issued (no backend card yet). */
|
||||
private fun issuingPlaceholderCard(orderId: String): TangemPayCard = TangemPayCard(
|
||||
id = orderId,
|
||||
productInstanceId = orderId,
|
||||
cardStatus = TangemPayCard.Status.INACTIVE,
|
||||
hasPinCode = false,
|
||||
displayName = null,
|
||||
limit = null,
|
||||
frozenState = TangemPayCardFrozenState.Unfrozen,
|
||||
lastDigits = "",
|
||||
state = TangemPayCardState.Issuing,
|
||||
)
|
||||
|
||||
private suspend fun VisaApiError.mapToPaymentAccountStatus(userWalletId: UserWalletId): PaymentAccountStatusValue {
|
||||
return when (this) {
|
||||
is VisaApiError.RefreshTokenExpired -> PaymentAccountStatusValue.Error.NotSynced
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.data.pay.repository
|
||||
|
||||
import com.tangem.datasource.local.visa.TangemPayIssueCardStore
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.repository.TangemPayIssueCardRepository
|
||||
import com.tangem.utils.coroutines.runSuspendCatching
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultIssueCardRepository @Inject constructor(
|
||||
private val tangemPayIssueCardStore: TangemPayIssueCardStore,
|
||||
) : TangemPayIssueCardRepository {
|
||||
|
||||
override suspend fun storeIssueOrderId(userWalletId: UserWalletId, orderId: String) {
|
||||
runSuspendCatching { tangemPayIssueCardStore.addIssueOrderId(userWalletId, orderId) }
|
||||
}
|
||||
|
||||
override suspend fun getIssueOrderIds(userWalletId: UserWalletId): List<String> {
|
||||
return runSuspendCatching { tangemPayIssueCardStore.getIssueOrderIds(userWalletId) }
|
||||
.getOrDefault(emptyList())
|
||||
}
|
||||
|
||||
override suspend fun removeIssueOrderId(userWalletId: UserWalletId, orderId: String) {
|
||||
runSuspendCatching { tangemPayIssueCardStore.removeIssueOrderId(userWalletId, orderId) }
|
||||
}
|
||||
}
|
||||
|
|
@ -20,18 +20,24 @@ enum class TangemPayCardState {
|
|||
/** A close order is in progress; the card is being closed. */
|
||||
@SerialName("Closing")
|
||||
Closing,
|
||||
|
||||
/** An issue order is in progress; the (additional) card is being issued and not yet provisioned. */
|
||||
@SerialName("Issuing")
|
||||
Issuing,
|
||||
;
|
||||
|
||||
override fun toString() = when (this) {
|
||||
Active -> "Active"
|
||||
Reissuing -> "Reissuing"
|
||||
Closing -> "Closing"
|
||||
Issuing -> "Issuing"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromString(value: String) = when (value.lowercase(Locale.US)) {
|
||||
"reissuing" -> Reissuing
|
||||
"closing" -> Closing
|
||||
"issuing" -> Issuing
|
||||
else -> Active
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.domain.pay.repository
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
/**
|
||||
* Tracks in-flight additional-card issuance orders locally.
|
||||
*
|
||||
* The backend does not return a card while it is still being issued, so the issuance order id is
|
||||
* stored locally to surface an "issuing" placeholder card until the order reaches a terminal state.
|
||||
*/
|
||||
interface TangemPayIssueCardRepository {
|
||||
|
||||
suspend fun storeIssueOrderId(userWalletId: UserWalletId, orderId: String)
|
||||
|
||||
suspend fun getIssueOrderIds(userWalletId: UserWalletId): List<String>
|
||||
|
||||
suspend fun removeIssueOrderId(userWalletId: UserWalletId, orderId: String)
|
||||
}
|
||||
|
|
@ -6,12 +6,15 @@ import arrow.core.raise.catch
|
|||
import arrow.core.raise.either
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.model.Offer
|
||||
import com.tangem.domain.pay.model.Order
|
||||
import com.tangem.domain.pay.model.TangemPayOrderInfo
|
||||
import com.tangem.domain.pay.repository.CustomerOffersRepository
|
||||
import com.tangem.domain.pay.repository.CustomerOrderRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayIssueCardRepository
|
||||
import com.tangem.domain.pay.util.OrderResolver
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
|
|
@ -33,8 +36,11 @@ import java.util.UUID
|
|||
class IssueAdditionalCardUseCase(
|
||||
private val customerOffersRepository: CustomerOffersRepository,
|
||||
private val customerOrderRepository: CustomerOrderRepository,
|
||||
private val issueCardRepository: TangemPayIssueCardRepository,
|
||||
private val startTangemPayOrderPollingUseCase: StartTangemPayOrderPollingUseCase,
|
||||
private val appCoroutineScope: AppCoroutineScope,
|
||||
) {
|
||||
suspend operator fun invoke(userWalletId: UserWalletId): Either<VisaApiError, Result> = either {
|
||||
suspend operator fun invoke(userWalletId: UserWalletId): Either<VisaApiError, Unit> = either {
|
||||
val offer = catch(
|
||||
block = {
|
||||
customerOffersRepository.getOffers(userWalletId)
|
||||
|
|
@ -61,20 +67,18 @@ class IssueAdditionalCardUseCase(
|
|||
idempotencyKey = UUID.randomUUID().toString(),
|
||||
).bind()
|
||||
|
||||
Result(order = order, offer = offer)
|
||||
issueCardRepository.storeIssueOrderId(userWalletId = userWalletId, orderId = order.id)
|
||||
|
||||
appCoroutineScope.launch {
|
||||
startTangemPayOrderPollingUseCase(
|
||||
order = TangemPayOrderInfo(orderId = order.id, orderStatus = order.status),
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Raise<VisaApiError>.handleError(throwable: Throwable): Nothing {
|
||||
TangemLogger.e("Error in IssueAdditionalCardUseCase", throwable)
|
||||
raise(VisaApiError.Unspecified)
|
||||
}
|
||||
|
||||
/**
|
||||
* Outcome of a successful run.
|
||||
*
|
||||
|
||||
* @property offer the offer that authorised issuance, carried back so the caller can show pricing
|
||||
* without an extra round trip.
|
||||
*/
|
||||
data class Result(val order: Order, val offer: Offer)
|
||||
}
|
||||
|
|
@ -10,7 +10,9 @@ import com.tangem.domain.pay.model.OrderStatus
|
|||
import com.tangem.domain.pay.model.OrderType
|
||||
import com.tangem.domain.pay.repository.CustomerOffersRepository
|
||||
import com.tangem.domain.pay.repository.CustomerOrderRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayIssueCardRepository
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import com.tangem.test.core.TestAppCoroutineScope
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
|
|
@ -23,7 +25,15 @@ internal class IssueAdditionalCardUseCaseTest {
|
|||
|
||||
private val offersRepository: CustomerOffersRepository = mockk()
|
||||
private val orderRepository: CustomerOrderRepository = mockk()
|
||||
private val useCase = IssueAdditionalCardUseCase(offersRepository, orderRepository)
|
||||
private val issueCardRepository: TangemPayIssueCardRepository = mockk(relaxed = true)
|
||||
private val startTangemPayOrderPollingUseCase: StartTangemPayOrderPollingUseCase = mockk(relaxed = true)
|
||||
private val useCase = IssueAdditionalCardUseCase(
|
||||
customerOffersRepository = offersRepository,
|
||||
customerOrderRepository = orderRepository,
|
||||
issueCardRepository = issueCardRepository,
|
||||
startTangemPayOrderPollingUseCase = startTangemPayOrderPollingUseCase,
|
||||
appCoroutineScope = TestAppCoroutineScope(),
|
||||
)
|
||||
private val userWalletId = UserWalletId("1234567890ABCDEF")
|
||||
private val spec = "SP_000004"
|
||||
|
||||
|
|
@ -62,10 +72,9 @@ internal class IssueAdditionalCardUseCaseTest {
|
|||
|
||||
val result = useCase(userWalletId)
|
||||
|
||||
val resultValue = result.getOrNull()
|
||||
assertThat(resultValue?.order).isEqualTo(existing)
|
||||
assertThat(resultValue?.offer).isEqualTo(offer)
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 0) { orderRepository.createOrder(any(), any(), any(), any()) }
|
||||
coVerify(exactly = 1) { issueCardRepository.storeIssueOrderId(userWalletId, existing.id) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -118,7 +127,8 @@ internal class IssueAdditionalCardUseCaseTest {
|
|||
|
||||
val result = useCase(userWalletId)
|
||||
|
||||
assertThat(result.getOrNull()?.order).isEqualTo(newOrder)
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 1) { issueCardRepository.storeIssueOrderId(userWalletId, newOrder.id) }
|
||||
}
|
||||
|
||||
private fun order(id: String, type: OrderType, status: OrderStatus): Order = Order(
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ internal class TangemPayDetailsStateFactory(
|
|||
}
|
||||
|
||||
fun getLoadedState(status: PaymentAccountStatusValue.Loaded): TangemPayDetailsUM {
|
||||
val card = status.cards.firstOrNull()
|
||||
val hasUnfrozenCard = status.cards.any { it.frozenState == TangemPayCardFrozenState.Unfrozen }
|
||||
val errorNotificationConfig = when (status.error) {
|
||||
null -> null
|
||||
PaymentAccountStatusValue.Error.NotSynced -> createRenewSessionNotificationConfig()
|
||||
|
|
@ -75,10 +75,7 @@ internal class TangemPayDetailsStateFactory(
|
|||
onRefresh = intents::onRefreshSwipe,
|
||||
),
|
||||
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(
|
||||
actionButtons = getActionButtonsConfig(
|
||||
isEnabled = errorNotificationConfig == null &&
|
||||
(card == null || card.frozenState == TangemPayCardFrozenState.Unfrozen),
|
||||
),
|
||||
actionButtons = getActionButtonsConfig(isEnabled = errorNotificationConfig == null && hasUnfrozenCard),
|
||||
cardsBlockState = TangemPayDetailsBalanceBlockState.CardsBlockState(
|
||||
cards = status.cards
|
||||
.let { if (isMultipleCardsEnabled) it else it.take(1) }
|
||||
|
|
@ -86,9 +83,11 @@ internal class TangemPayDetailsStateFactory(
|
|||
TangemPayDetailsBalanceBlockState.Card(
|
||||
lastDigits = cardItem.lastDigits,
|
||||
onClick = { intents.onCardClick(cardItem.id) },
|
||||
isReissuing = cardItem.state != TangemPayCardState.Active,
|
||||
isReissuingOrClosing = cardItem.state == TangemPayCardState.Reissuing ||
|
||||
cardItem.state == TangemPayCardState.Closing,
|
||||
isEnabled = errorNotificationConfig == null,
|
||||
isFrozen = cardItem.isFrozen,
|
||||
isIssuing = cardItem.state == TangemPayCardState.Issuing,
|
||||
)
|
||||
}
|
||||
.toImmutableList(),
|
||||
|
|
|
|||
|
|
@ -94,7 +94,8 @@ internal sealed class TangemPayDetailsBalanceBlockState {
|
|||
data class Card(
|
||||
val lastDigits: String,
|
||||
val onClick: () -> Unit,
|
||||
val isReissuing: Boolean,
|
||||
val isReissuingOrClosing: Boolean,
|
||||
val isIssuing: Boolean,
|
||||
val isFrozen: Boolean,
|
||||
val isEnabled: Boolean,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -207,6 +207,12 @@ private fun LazyListScope.cardState(state: TangemPayCardPageUM) {
|
|||
subtitle = resourceReference(R.string.tangempay_card_page_closing_banner_description),
|
||||
)
|
||||
}
|
||||
TangemPayCardState.Issuing -> cardPageItem(key = "Issuing") {
|
||||
TangemPayReplacingCardBlock(
|
||||
title = resourceReference(R.string.tangempay_issuing_new_digital_card_title),
|
||||
subtitle = resourceReference(R.string.tangempay_reissue_card_in_progress_description),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ internal fun TangemPayDetailsScreen(
|
|||
},
|
||||
)
|
||||
|
||||
if (state.balanceBlockState.cardsBlockState?.cards?.fastAny { it.isReissuing } == true) {
|
||||
if (state.balanceBlockState.cardsBlockState?.cards?.fastAny { it.isReissuingOrClosing } == true) {
|
||||
item(
|
||||
key = "REISSUE_MESSAGE",
|
||||
content = {
|
||||
|
|
@ -328,7 +328,7 @@ private fun TangemPayCardItem(card: TangemPayDetailsBalanceBlockState.Card, modi
|
|||
.alpha(if (card.isEnabled) 1f else DISABLED_ALPHA)
|
||||
.fillMaxSize(),
|
||||
painter = painterResource(
|
||||
if (card.isReissuing) {
|
||||
if (card.isReissuingOrClosing) {
|
||||
R.drawable.img_visa_card_inactive_48_32
|
||||
} else {
|
||||
R.drawable.img_visa_card_48_32
|
||||
|
|
@ -336,7 +336,7 @@ private fun TangemPayCardItem(card: TangemPayDetailsBalanceBlockState.Card, modi
|
|||
),
|
||||
contentDescription = null,
|
||||
)
|
||||
if (!card.isReissuing) {
|
||||
if (!card.isReissuingOrClosing) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
|
|
@ -485,16 +485,18 @@ internal class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<T
|
|||
TangemPayDetailsBalanceBlockState.Card(
|
||||
lastDigits = "1234",
|
||||
onClick = {},
|
||||
isReissuing = false,
|
||||
isReissuingOrClosing = false,
|
||||
isEnabled = false,
|
||||
isFrozen = false,
|
||||
isIssuing = false,
|
||||
),
|
||||
TangemPayDetailsBalanceBlockState.Card(
|
||||
lastDigits = "3456",
|
||||
onClick = {},
|
||||
isReissuing = false,
|
||||
isReissuingOrClosing = false,
|
||||
isEnabled = true,
|
||||
isFrozen = false,
|
||||
isIssuing = false,
|
||||
),
|
||||
),
|
||||
onAddCardClick = {},
|
||||
|
|
@ -523,9 +525,10 @@ internal class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<T
|
|||
TangemPayDetailsBalanceBlockState.Card(
|
||||
lastDigits = "1234",
|
||||
onClick = {},
|
||||
isReissuing = true,
|
||||
isReissuingOrClosing = true,
|
||||
isFrozen = false,
|
||||
isEnabled = true,
|
||||
isIssuing = false,
|
||||
),
|
||||
),
|
||||
onAddCardClick = {},
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ private fun LazyListScope.payDetailsBody(state: TangemPayDetailsUM) {
|
|||
}
|
||||
}
|
||||
when {
|
||||
state.balanceBlockState.cardsBlockState?.cards?.fastAny { it.isReissuing } == true -> {
|
||||
state.balanceBlockState.cardsBlockState?.cards?.fastAny { it.isReissuingOrClosing } == true -> {
|
||||
item("reissuingBannerBlock") {
|
||||
TangemMessage(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4),
|
||||
|
|
@ -167,6 +167,15 @@ private fun LazyListScope.payDetailsBody(state: TangemPayDetailsUM) {
|
|||
)
|
||||
}
|
||||
}
|
||||
state.balanceBlockState.cardsBlockState?.cards?.fastAny { it.isIssuing } == true -> {
|
||||
item("issuingBannerBlock") {
|
||||
TangemMessage(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4),
|
||||
title = resourceReference(R.string.tangempay_issuing_new_digital_card_title),
|
||||
subtitle = resourceReference(R.string.tangempay_reissue_card_in_progress_description),
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if (state.errorNotificationConfig != null) {
|
||||
item("errorSessionBannerBlock") {
|
||||
|
|
@ -342,7 +351,7 @@ private fun CardsBlock(
|
|||
) {
|
||||
items(items = cardsBlockState.cards) { item ->
|
||||
TangemPayCardView(
|
||||
isReissuing = item.isReissuing,
|
||||
isIssueInProgress = item.isIssuing || item.isReissuingOrClosing,
|
||||
lastDigits = item.lastDigits,
|
||||
onClick = item.onClick,
|
||||
isEnabled = item.isEnabled,
|
||||
|
|
|
|||
|
|
@ -1,211 +1,271 @@
|
|||
package com.tangem.features.tangempay.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconEnd
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.ds2.button.TangemButton
|
||||
import com.tangem.core.ui.ds2.row.TangemRow
|
||||
import com.tangem.core.ui.ds2.row.TangemRowContentLead
|
||||
import com.tangem.core.ui.ds2.row.TangemRowText
|
||||
import com.tangem.core.ui.ds2.row.TangemRowTextRole
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.res.generated.icons.Icons
|
||||
import com.tangem.core.ui.res.generated.icons.ic_card_plus_32
|
||||
import com.tangem.core.ui.res.generated.icons.ic_error_28
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayIssueAdditionalCardUM
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayIssueAdditionalCardContent(state: TangemPayIssueAdditionalCardUM) {
|
||||
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = state.onDismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
onBack = state.onDismiss,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
type = TangemBottomSheetType.Modal,
|
||||
containerColor = TangemTheme.colors3.bg.secondary,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = state.onDismiss,
|
||||
TangemTopBar(
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
endContent = {
|
||||
TangemButton(
|
||||
iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_close_24),
|
||||
onClick = state.onDismiss,
|
||||
size = TangemButton.Size.X11,
|
||||
variant = TangemButton.Variant.Material,
|
||||
)
|
||||
},
|
||||
) {
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Content(state)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(state: TangemPayIssueAdditionalCardUM) {
|
||||
val appearance = state.contentAppearance()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
SpacerH(16.dp)
|
||||
StatusIcon(appearance = appearance)
|
||||
SpacerH(32.dp)
|
||||
CenteredMessageText(
|
||||
textRes = appearance.titleRes,
|
||||
style = TangemTheme.typography3.heading.small,
|
||||
color = TangemTheme.colors3.text.primary,
|
||||
)
|
||||
CenteredMessageText(
|
||||
textRes = appearance.subtitleRes,
|
||||
style = TangemTheme.typography3.subheading.medium,
|
||||
color = TangemTheme.colors3.text.secondary,
|
||||
)
|
||||
SpacerH(32.dp)
|
||||
FeeBlock(modifier = Modifier.padding(top = 16.dp), state = state)
|
||||
SpacerH(8.dp)
|
||||
BottomButtonsBlock(state = state, appearance = appearance)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StatusIcon(appearance: IssueAdditionalCardContentAppearance) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(80.dp)
|
||||
.clip(CircleShape)
|
||||
.background(appearance.iconBackgroundColor),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = appearance.icon,
|
||||
contentDescription = null,
|
||||
tint = appearance.iconColor,
|
||||
modifier = Modifier.size(28.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CenteredMessageText(textRes: Int, style: TextStyle, color: Color) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
CardIcon()
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
text = stringResourceSafe(R.string.tangempay_issue_additional_card_title),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
text = stringResourceSafe(textRes),
|
||||
style = style,
|
||||
color = color,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 4.dp),
|
||||
text = stringResourceSafe(R.string.tangempay_issue_additional_card_description),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
FeeRow(modifier = Modifier.padding(top = 16.dp), state = state)
|
||||
if (state.isBalanceInsufficient) {
|
||||
InsufficientFundsBlock(modifier = Modifier.padding(top = 8.dp), onAddFundsClick = state.onAddFundsClick)
|
||||
}
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResourceSafe(R.string.tangempay_issue_card),
|
||||
showProgress = state.isLoading,
|
||||
enabled = !state.isLoading && !state.isBalanceInsufficient,
|
||||
onClick = state.onIssueClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CardIcon() {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f),
|
||||
shape = CircleShape,
|
||||
private fun FeeBlock(state: TangemPayIssueAdditionalCardUM, modifier: Modifier = Modifier) {
|
||||
TangemRow(
|
||||
modifier = modifier,
|
||||
contentLead = TangemRowContentLead.Start,
|
||||
titleSlot = {
|
||||
TangemRowText(
|
||||
text = resourceReference(R.string.tangempay_issue_additional_card_fee_label),
|
||||
role = TangemRowTextRole.Title,
|
||||
)
|
||||
.size(48.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_credit_card_add_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeeRow(state: TangemPayIssueAdditionalCardUM, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResourceSafe(R.string.tangempay_issue_additional_card_fee_label),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
},
|
||||
valueSlot = {
|
||||
TangemRowText(
|
||||
text = state.feeText,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
role = TangemRowTextRole.Value,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InsufficientFundsBlock(onAddFundsClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
private fun BottomButtonsBlock(
|
||||
state: TangemPayIssueAdditionalCardUM,
|
||||
appearance: IssueAdditionalCardContentAppearance,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
.padding(top = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.Top) {
|
||||
Image(
|
||||
modifier = Modifier.size(20.dp),
|
||||
painter = painterResource(id = R.drawable.img_usdc_16),
|
||||
contentDescription = null,
|
||||
TangemButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
variant = TangemButton.Variant.Secondary,
|
||||
size = TangemButton.Size.X12,
|
||||
onClick = state.onDismiss,
|
||||
text = resourceReference(R.string.common_cancel),
|
||||
)
|
||||
Column(modifier = Modifier.padding(start = 12.dp)) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.tangempay_reissue_card_insufficient_funds_title),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(
|
||||
R.string.tangempay_reissue_card_insufficient_funds_subtitle,
|
||||
),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
SecondaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp),
|
||||
text = stringResourceSafe(R.string.common_add_funds),
|
||||
iconResId = R.drawable.ic_plus_24,
|
||||
onClick = onAddFundsClick,
|
||||
TangemButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
size = TangemButton.Size.X12,
|
||||
onClick = appearance.primaryAction(state),
|
||||
isEnabled = !state.isLoading,
|
||||
isLoading = state.isLoading,
|
||||
text = resourceReference(appearance.primaryButtonTextRes),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private data class IssueAdditionalCardContentAppearance(
|
||||
val titleRes: Int,
|
||||
val subtitleRes: Int,
|
||||
val icon: ImageVector,
|
||||
val iconColor: Color,
|
||||
val iconBackgroundColor: Color,
|
||||
val primaryButtonTextRes: Int,
|
||||
val primaryAction: (TangemPayIssueAdditionalCardUM) -> () -> Unit,
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun TangemPayIssueAdditionalCardUM.contentAppearance(): IssueAdditionalCardContentAppearance {
|
||||
return if (isBalanceInsufficient) {
|
||||
IssueAdditionalCardContentAppearance(
|
||||
titleRes = R.string.tangempay_reissue_card_insufficient_funds_title,
|
||||
subtitleRes = R.string.tangempay_reissue_card_insufficient_funds_subtitle,
|
||||
icon = Icons.ic_error_28,
|
||||
iconColor = TangemTheme.colors3.icon.status.warning,
|
||||
iconBackgroundColor = TangemTheme.colors3.bg.status.warningSubtle,
|
||||
primaryButtonTextRes = R.string.tangempay_card_details_add_funds,
|
||||
primaryAction = { it.onAddFundsClick },
|
||||
)
|
||||
} else {
|
||||
IssueAdditionalCardContentAppearance(
|
||||
titleRes = R.string.tangempay_issue_additional_card_title,
|
||||
subtitleRes = R.string.tangempay_issue_additional_card_description,
|
||||
icon = Icons.ic_card_plus_32,
|
||||
iconColor = TangemTheme.colors3.icon.status.info,
|
||||
iconBackgroundColor = TangemTheme.colors3.bg.status.infoSubtle,
|
||||
primaryButtonTextRes = R.string.tangempay_issue_card,
|
||||
primaryAction = { it.onIssueClick },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun TangemPayIssueAdditionalCardContentPreview(
|
||||
@PreviewParameter(IssueAdditionalCardPreviewProvider::class) state: TangemPayIssueAdditionalCardUM,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
TangemPayIssueAdditionalCardContent(state = state)
|
||||
TangemThemePreviewRedesign {
|
||||
IssueAdditionalCardSheetPreview(state = state)
|
||||
}
|
||||
}
|
||||
|
||||
private class IssueAdditionalCardPreviewProvider : PreviewParameterProvider<TangemPayIssueAdditionalCardUM> {
|
||||
override val values: Sequence<TangemPayIssueAdditionalCardUM>
|
||||
get() = sequenceOf(
|
||||
TangemPayIssueAdditionalCardUM(
|
||||
isBalanceInsufficient = false,
|
||||
feeText = "10 $",
|
||||
isLoading = false,
|
||||
onIssueClick = {},
|
||||
onAddFundsClick = {},
|
||||
onDismiss = {},
|
||||
),
|
||||
TangemPayIssueAdditionalCardUM(
|
||||
isBalanceInsufficient = true,
|
||||
feeText = "10 $",
|
||||
isLoading = false,
|
||||
onIssueClick = {},
|
||||
onAddFundsClick = {},
|
||||
onDismiss = {},
|
||||
),
|
||||
TangemPayIssueAdditionalCardUM(
|
||||
isBalanceInsufficient = false,
|
||||
feeText = "10 $",
|
||||
isLoading = true,
|
||||
onIssueClick = {},
|
||||
onAddFundsClick = {},
|
||||
onDismiss = {},
|
||||
@Composable
|
||||
private fun IssueAdditionalCardSheetPreview(state: TangemPayIssueAdditionalCardUM) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(TangemTheme.colors3.bg.secondary),
|
||||
) {
|
||||
TangemTopBar(
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
endContent = {
|
||||
TangemButton(
|
||||
iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_close_24),
|
||||
onClick = state.onDismiss,
|
||||
size = TangemButton.Size.X11,
|
||||
variant = TangemButton.Variant.Material,
|
||||
)
|
||||
},
|
||||
)
|
||||
Content(state)
|
||||
}
|
||||
}
|
||||
|
||||
private class IssueAdditionalCardPreviewProvider :
|
||||
CollectionPreviewParameterProvider<TangemPayIssueAdditionalCardUM>(
|
||||
collection = listOf(
|
||||
stubState(),
|
||||
stubState(isBalanceInsufficient = true),
|
||||
stubState(isLoading = true),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun stubState(
|
||||
isBalanceInsufficient: Boolean = false,
|
||||
isLoading: Boolean = false,
|
||||
): TangemPayIssueAdditionalCardUM = TangemPayIssueAdditionalCardUM(
|
||||
isBalanceInsufficient = isBalanceInsufficient,
|
||||
feeText = "$4.25",
|
||||
isLoading = isLoading,
|
||||
onIssueClick = {},
|
||||
onAddFundsClick = {},
|
||||
onDismiss = {},
|
||||
)
|
||||
|
|
@ -41,7 +41,7 @@ private const val REISSUING_CARD_BG = 0xFF1E1E1E
|
|||
|
||||
@Composable
|
||||
internal fun TangemPayCardView(
|
||||
isReissuing: Boolean,
|
||||
isIssueInProgress: Boolean,
|
||||
isEnabled: Boolean,
|
||||
lastDigits: String,
|
||||
onClick: () -> Unit,
|
||||
|
|
@ -55,7 +55,7 @@ internal fun TangemPayCardView(
|
|||
width = TangemTheme.dimens2.x14,
|
||||
)
|
||||
.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_CARD_BUTTON),
|
||||
isReissuing = isReissuing,
|
||||
isIssueInProgress = isIssueInProgress,
|
||||
isEnabled = isEnabled,
|
||||
onClick = onClick,
|
||||
) {
|
||||
|
|
@ -71,7 +71,7 @@ internal fun TangemPayCardView(
|
|||
modifier = Modifier.size(TangemTheme.dimens2.x3),
|
||||
imageVector = when {
|
||||
isFrozen -> Icons.ic_snowflake_16
|
||||
isReissuing -> Icons.ic_clock_12
|
||||
isIssueInProgress -> Icons.ic_clock_12
|
||||
else -> Icons.ic_cloud_12_filled
|
||||
},
|
||||
tint = TangemTheme.colors3.icon.staticDark,
|
||||
|
|
@ -122,14 +122,14 @@ internal fun TangemPayAddCardView(onClick: () -> Unit, modifier: Modifier = Modi
|
|||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun CardBackground(
|
||||
isReissuing: Boolean,
|
||||
isIssueInProgress: Boolean,
|
||||
isEnabled: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
) {
|
||||
val bgColor = remember(isReissuing) {
|
||||
if (isReissuing) {
|
||||
val bgColor = remember(isIssueInProgress) {
|
||||
if (isIssueInProgress) {
|
||||
Color(REISSUING_CARD_BG)
|
||||
} else {
|
||||
Color(DEFAULT_CARD_BG)
|
||||
|
|
@ -151,9 +151,9 @@ private fun CardBackground(
|
|||
brush = Brush.radialGradient(
|
||||
colors = listOf(
|
||||
Color(
|
||||
if (isReissuing) 0xFFB0B4BC else 0xFF38587F,
|
||||
if (isIssueInProgress) 0xFFB0B4BC else 0xFF38587F,
|
||||
).copy(
|
||||
if (isReissuing) .1f else .41f,
|
||||
if (isIssueInProgress) .1f else .41f,
|
||||
),
|
||||
Color.Transparent,
|
||||
),
|
||||
|
|
@ -183,7 +183,7 @@ private fun CardBackgroundPreview() {
|
|||
height = TangemTheme.dimens2.x10,
|
||||
width = TangemTheme.dimens2.x14,
|
||||
),
|
||||
isReissuing = false,
|
||||
isIssueInProgress = false,
|
||||
onClick = {},
|
||||
content = {},
|
||||
isEnabled = true,
|
||||
|
|
@ -194,14 +194,14 @@ private fun CardBackgroundPreview() {
|
|||
height = TangemTheme.dimens2.x10,
|
||||
width = TangemTheme.dimens2.x14,
|
||||
),
|
||||
isReissuing = true,
|
||||
isIssueInProgress = true,
|
||||
onClick = {},
|
||||
content = {},
|
||||
isEnabled = true,
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x4)
|
||||
TangemPayCardView(
|
||||
isReissuing = false,
|
||||
isIssueInProgress = false,
|
||||
onClick = {},
|
||||
lastDigits = "1234",
|
||||
isEnabled = true,
|
||||
|
|
@ -209,7 +209,7 @@ private fun CardBackgroundPreview() {
|
|||
)
|
||||
SpacerH(TangemTheme.dimens2.x4)
|
||||
TangemPayCardView(
|
||||
isReissuing = true,
|
||||
isIssueInProgress = true,
|
||||
onClick = {},
|
||||
lastDigits = "",
|
||||
isEnabled = true,
|
||||
|
|
@ -218,7 +218,13 @@ private fun CardBackgroundPreview() {
|
|||
SpacerH(TangemTheme.dimens2.x4)
|
||||
TangemPayAddCardView(onClick = {})
|
||||
SpacerH(TangemTheme.dimens2.x4)
|
||||
TangemPayCardView(isReissuing = false, onClick = {}, lastDigits = "1234", isEnabled = true, isFrozen = true)
|
||||
TangemPayCardView(
|
||||
isIssueInProgress = false,
|
||||
onClick = {},
|
||||
lastDigits = "1234",
|
||||
isEnabled = true,
|
||||
isFrozen = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import com.tangem.core.ui.message.BottomSheetMessage
|
|||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.core.ui.res.generated.icons.Icons
|
||||
import com.tangem.core.ui.res.generated.icons.ic_error_28
|
||||
import com.tangem.core.ui.res.generated.icons.ic_snowflake_20
|
||||
import com.tangem.core.ui.res.generated.icons.ic_sun_20
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
|
||||
|
|
@ -152,8 +153,9 @@ internal object TangemPayMessagesFactory {
|
|||
fun createMaximumCardsIssued(maxCards: Int): BottomSheetMessage {
|
||||
return bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_attention_20) {
|
||||
backgroundType = MessageBottomSheetUM.Icon.BackgroundType.Attention
|
||||
vector(Icons.ic_error_28) {
|
||||
type = MessageBottomSheetUM.Vector.Type.Attention
|
||||
backgroundType = MessageBottomSheetUM.Vector.BackgroundType.Attention
|
||||
}
|
||||
title = resourceReference(R.string.tangempay_maximum_cards_issued_title)
|
||||
body = resourceReference(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue