diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/TangemPayStoresModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/TangemPayStoresModule.kt new file mode 100644 index 0000000000..ae3097d006 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/TangemPayStoresModule.kt @@ -0,0 +1,23 @@ +package com.tangem.datasource.di + +import com.tangem.datasource.local.datastore.RuntimeDataStore +import com.tangem.datasource.local.visa.DefaultTangemPayCardFrozenStateStore +import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object TangemPayStoresModule { + + @Provides + @Singleton + fun provideTangemPayCardFrozenStateStore(): TangemPayCardFrozenStateStore { + return DefaultTangemPayCardFrozenStateStore( + dataStore = RuntimeDataStore(), + ) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/visa/DefaultTangemPayCardFrozenStateStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/visa/DefaultTangemPayCardFrozenStateStore.kt new file mode 100644 index 0000000000..4eefede01a --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/visa/DefaultTangemPayCardFrozenStateStore.kt @@ -0,0 +1,21 @@ +package com.tangem.datasource.local.visa + +import com.tangem.datasource.local.datastore.core.StringKeyDataStore +import com.tangem.domain.visa.model.TangemPayCardFrozenState +import kotlinx.coroutines.flow.Flow + +internal class DefaultTangemPayCardFrozenStateStore( + private val dataStore: StringKeyDataStore, +) : TangemPayCardFrozenStateStore { + override suspend fun getSyncOrNull(key: String): TangemPayCardFrozenState? { + return dataStore.getSyncOrNull(key) + } + + override fun get(key: String): Flow { + return dataStore.get(key) + } + + override suspend fun store(key: String, value: TangemPayCardFrozenState) { + dataStore.store(key, value) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/visa/TangemPayCardFrozenStateStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/visa/TangemPayCardFrozenStateStore.kt new file mode 100644 index 0000000000..61f4f1b0c0 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/visa/TangemPayCardFrozenStateStore.kt @@ -0,0 +1,13 @@ +package com.tangem.datasource.local.visa + +import com.tangem.domain.visa.model.TangemPayCardFrozenState +import kotlinx.coroutines.flow.Flow + +interface TangemPayCardFrozenStateStore { + + suspend fun getSyncOrNull(key: String): TangemPayCardFrozenState? + + fun get(key: String): Flow + + suspend fun store(key: String, value: TangemPayCardFrozenState) +} \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/img_frozen_tangem_pay_visa.webp b/core/ui/src/main/res/drawable/img_frozen_tangem_pay_visa.webp new file mode 100644 index 0000000000..464da3a41e Binary files /dev/null and b/core/ui/src/main/res/drawable/img_frozen_tangem_pay_visa.webp differ diff --git a/core/ui/src/main/res/drawable/img_tangem_pay_visa.webp b/core/ui/src/main/res/drawable/img_tangem_pay_visa.webp index ba8b70f6d3..2ceacdaa2b 100644 Binary files a/core/ui/src/main/res/drawable/img_tangem_pay_visa.webp and b/core/ui/src/main/res/drawable/img_tangem_pay_visa.webp differ diff --git a/core/utils/src/main/java/com/tangem/utils/StringsSigns.kt b/core/utils/src/main/java/com/tangem/utils/StringsSigns.kt index 98ba454b64..90655e7d52 100644 --- a/core/utils/src/main/java/com/tangem/utils/StringsSigns.kt +++ b/core/utils/src/main/java/com/tangem/utils/StringsSigns.kt @@ -14,6 +14,7 @@ object StringsSigns { const val NON_BREAKING_SPACE = '\u00A0' const val PERCENT = "%" const val THREE_STARS = "\u2217\u2217\u2217" + const val ASTERISK = "\u2217" const val PASSWORD_VISUAL_CHAR = '\u2022' const val APPROXIMATE = "≈" const val WHITE_SPACE = " " diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt index 5167b4adee..05c3965563 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt @@ -8,6 +8,7 @@ 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.response.CustomerMeResponse +import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore import com.tangem.datasource.local.visa.TangemPayStorage import com.tangem.domain.pay.datasource.TangemPayAuthDataSource import com.tangem.domain.pay.model.CustomerInfo @@ -16,6 +17,7 @@ import com.tangem.domain.pay.model.CustomerInfo.ProductInstance import com.tangem.domain.pay.model.MainScreenCustomerInfo import com.tangem.domain.pay.model.OrderStatus import com.tangem.domain.pay.repository.OnboardingRepository +import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext import javax.inject.Inject @@ -24,6 +26,7 @@ private const val VALID_STATUS = "valid" private const val APPROVED_KYC_STATUS = "APPROVED" private const val TAG = "TangemPay: OnboardingRepository" +@Suppress("LongParameterList") internal class DefaultOnboardingRepository @Inject constructor( private val dispatcherProvider: CoroutineDispatcherProvider, private val tangemPayApi: TangemPayApi, @@ -31,6 +34,7 @@ internal class DefaultOnboardingRepository @Inject constructor( private val tangemPayStorage: TangemPayStorage, private val authDataSource: TangemPayAuthDataSource, private val tangemPayWalletsManager: TangemPayWalletsManager, + private val cardFrozenStateStore: TangemPayCardFrozenStateStore, ) : OnboardingRepository { override suspend fun validateDeeplink(link: String): Either { @@ -122,7 +126,7 @@ internal class DefaultOnboardingRepository @Inject constructor( } } - private fun getCustomerInfo(response: CustomerMeResponse.Result?): CustomerInfo { + private suspend fun getCustomerInfo(response: CustomerMeResponse.Result?): CustomerInfo { val card = response?.card val balance = response?.balance val paymentAccount = response?.paymentAccount @@ -138,6 +142,13 @@ internal class DefaultOnboardingRepository @Inject constructor( null } val productInstance = response?.productInstance?.let { instance -> + cardFrozenStateStore.store( + key = instance.cardId, + value = when (instance.status) { + CustomerMeResponse.ProductInstance.Status.ACTIVE -> TangemPayCardFrozenState.Unfrozen + else -> TangemPayCardFrozenState.Frozen + }, + ) ProductInstance( id = instance.id, cardId = instance.cardId, diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayCardDetailsRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayCardDetailsRepository.kt index 56b74801a3..1d9a850f24 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayCardDetailsRepository.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayCardDetailsRepository.kt @@ -12,18 +12,19 @@ import com.tangem.datasource.api.pay.models.request.CardDetailsRequest import com.tangem.datasource.api.pay.models.request.FreezeUnfreezeCardRequest import com.tangem.datasource.api.pay.models.request.SetPinRequest import com.tangem.datasource.api.pay.models.response.FreezeUnfreezeCardResponse +import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore import com.tangem.datasource.local.visa.TangemPayStorage import com.tangem.domain.pay.model.SetPinResult import com.tangem.domain.pay.model.TangemPayCardBalance import com.tangem.domain.pay.model.TangemPayCardDetails import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository -import com.tangem.domain.visa.error.VisaApiError -import timber.log.Timber +import com.tangem.domain.visa.model.TangemPayCardFrozenState +import kotlinx.coroutines.flow.Flow import javax.inject.Inject private const val TAG = "TangemPay: CardDetailsRepository" -@Suppress("OptionalUnit") +@Suppress("LongParameterList") internal class DefaultTangemPayCardDetailsRepository @Inject constructor( private val tangemPayApi: TangemPayApi, private val requestHelper: TangemPayRequestPerformer, @@ -31,6 +32,7 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor( private val apiConfigsManager: ApiConfigsManager, private val rainCryptoUtil: RainCryptoUtil, private val storage: TangemPayStorage, + private val cardFrozenStateStore: TangemPayCardFrozenStateStore, ) : TangemPayCardDetailsRepository { override suspend fun getCardBalance(): Either { @@ -117,34 +119,58 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor( } } - override suspend fun freezeCard(cardId: String): Either { + override suspend fun freezeCard(cardId: String): Either { + cardFrozenStateStore.store(cardId, TangemPayCardFrozenState.Pending) return requestHelper.makeSafeRequest { tangemPayApi.freezeCard(authHeader = it, body = FreezeUnfreezeCardRequest(cardId = cardId)) + }.onLeft { + cardFrozenStateStore.store(cardId, TangemPayCardFrozenState.Unfrozen) }.map { response -> - val result = response.result - if (result != null && result.status == FreezeUnfreezeCardResponse.Status.COMPLETED) { - Either.Right(Unit) - } else { - Timber.e("Card freeze failed with status: ${response.result?.status}") - Either.Left(VisaApiError.UnknownWithoutCode) + val state = when (response.result?.status) { + FreezeUnfreezeCardResponse.Status.COMPLETED -> TangemPayCardFrozenState.Frozen + FreezeUnfreezeCardResponse.Status.NEW, + FreezeUnfreezeCardResponse.Status.PROCESSING, + -> TangemPayCardFrozenState.Pending + FreezeUnfreezeCardResponse.Status.CANCELED, + null, + -> TangemPayCardFrozenState.Unfrozen } + cardFrozenStateStore.store(cardId, state) + + state } } - override suspend fun unfreezeCard(cardId: String): Either { + override suspend fun unfreezeCard(cardId: String): Either { + cardFrozenStateStore.store(cardId, TangemPayCardFrozenState.Pending) return requestHelper.makeSafeRequest { tangemPayApi.unfreezeCard(authHeader = it, body = FreezeUnfreezeCardRequest(cardId = cardId)) + }.onLeft { + cardFrozenStateStore.store(cardId, TangemPayCardFrozenState.Frozen) }.map { response -> - val result = response.result - if (result != null && result.status == FreezeUnfreezeCardResponse.Status.COMPLETED) { - Either.Right(Unit) - } else { - Timber.e("Card unfreeze failed with status: ${response.result?.status}") - Either.Left(VisaApiError.UnknownWithoutCode) + val state = when (response.result?.status) { + FreezeUnfreezeCardResponse.Status.COMPLETED -> TangemPayCardFrozenState.Unfrozen + FreezeUnfreezeCardResponse.Status.NEW, + FreezeUnfreezeCardResponse.Status.PROCESSING, + -> TangemPayCardFrozenState.Pending + FreezeUnfreezeCardResponse.Status.CANCELED, + null, + -> TangemPayCardFrozenState.Frozen } + cardFrozenStateStore.store(cardId, state) + + state } } + override fun cardFrozenState(cardId: String): Flow { + return cardFrozenStateStore.get(cardId) + } + + override suspend fun cardFrozenStateSync(cardId: String): TangemPayCardFrozenState? { + return cardFrozenStateStore.getSyncOrNull(cardId) + } + private suspend fun getPublicKeyBase64(): String { val env = apiConfigsManager.getEnvironmentConfig(ApiConfig.ID.TangemPay).environment return when (env) { diff --git a/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt index b8d8fcc74d..1a31841f25 100644 --- a/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt +++ b/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt @@ -1,11 +1,12 @@ package com.tangem.domain.pay +import com.tangem.domain.visa.model.TangemPayCardFrozenState import kotlinx.serialization.Serializable @Serializable data class TangemPayDetailsConfig( val cardId: String, - val isCardFrozen: Boolean, + val cardFrozenState: TangemPayCardFrozenState, val customerWalletAddress: String, val cardNumberEnd: String, val chainId: Int, diff --git a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/TangemPayCardFrozenState.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/TangemPayCardFrozenState.kt new file mode 100644 index 0000000000..6cbdffa944 --- /dev/null +++ b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/TangemPayCardFrozenState.kt @@ -0,0 +1,10 @@ +package com.tangem.domain.visa.model + +import kotlinx.serialization.Serializable + +@Serializable +sealed class TangemPayCardFrozenState { + data object Pending : TangemPayCardFrozenState() + data object Frozen : TangemPayCardFrozenState() + data object Unfrozen : TangemPayCardFrozenState() +} \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/repository/TangemPayCardDetailsRepository.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/repository/TangemPayCardDetailsRepository.kt index 9732d15868..6ce352de48 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/repository/TangemPayCardDetailsRepository.kt +++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/repository/TangemPayCardDetailsRepository.kt @@ -5,6 +5,8 @@ import com.tangem.core.error.UniversalError import com.tangem.domain.pay.model.SetPinResult import com.tangem.domain.pay.model.TangemPayCardBalance import com.tangem.domain.pay.model.TangemPayCardDetails +import com.tangem.domain.visa.model.TangemPayCardFrozenState +import kotlinx.coroutines.flow.Flow interface TangemPayCardDetailsRepository { @@ -18,6 +20,8 @@ interface TangemPayCardDetailsRepository { suspend fun setAddToWalletAsDone(): Either - suspend fun freezeCard(cardId: String): Either - suspend fun unfreezeCard(cardId: String): Either + suspend fun freezeCard(cardId: String): Either + suspend fun unfreezeCard(cardId: String): Either + fun cardFrozenState(cardId: String): Flow + suspend fun cardFrozenStateSync(cardId: String): TangemPayCardFrozenState? } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/DefaultTangemPayCardDetailsBlockComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/DefaultTangemPayCardDetailsBlockComponent.kt index a044336b19..95e5cae0d4 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/DefaultTangemPayCardDetailsBlockComponent.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/DefaultTangemPayCardDetailsBlockComponent.kt @@ -6,7 +6,7 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM import com.tangem.features.tangempay.model.TangemPayCardDetailsBlockModel -import com.tangem.features.tangempay.ui.CardDetailsItem +import com.tangem.features.tangempay.ui.TangemPayCard import kotlinx.coroutines.flow.StateFlow internal class DefaultTangemPayCardDetailsBlockComponent( @@ -19,6 +19,6 @@ internal class DefaultTangemPayCardDetailsBlockComponent( @Composable override fun CardDetailsBlockContent(state: TangemPayCardDetailsUM, modifier: Modifier) { - CardDetailsItem(state, modifier) + TangemPayCard(state, modifier) } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/PreviewTangemPayCardDetailsBlockComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/PreviewTangemPayCardDetailsBlockComponent.kt index 861195443a..8a4043c5ee 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/PreviewTangemPayCardDetailsBlockComponent.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/PreviewTangemPayCardDetailsBlockComponent.kt @@ -3,7 +3,7 @@ package com.tangem.features.tangempay.components.cardDetails import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM -import com.tangem.features.tangempay.ui.CardDetailsItem +import com.tangem.features.tangempay.ui.TangemPayCard import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -15,6 +15,6 @@ internal class PreviewTangemPayCardDetailsBlockComponent( @Composable override fun CardDetailsBlockContent(state: TangemPayCardDetailsUM, modifier: Modifier) { - CardDetailsItem(state, modifier) + TangemPayCard(state, modifier) } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/TangemPayCardDetailsBlockComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/TangemPayCardDetailsBlockComponent.kt index 60f012fd59..2714c0a662 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/TangemPayCardDetailsBlockComponent.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/cardDetails/TangemPayCardDetailsBlockComponent.kt @@ -1,11 +1,13 @@ 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.features.tangempay.entity.TangemPayCardDetailsUM import kotlinx.coroutines.flow.StateFlow +@Stable internal interface TangemPayCardDetailsBlockComponent { val state: StateFlow diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayCardDetailsBlockStateFactory.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayCardDetailsBlockStateFactory.kt index c2ad5c05fb..0d2e66653a 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayCardDetailsBlockStateFactory.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayCardDetailsBlockStateFactory.kt @@ -1,33 +1,24 @@ package com.tangem.features.tangempay.entity -import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.features.tangempay.details.impl.R -import com.tangem.features.tangempay.utils.CardDetailsFormatUtil import com.tangem.utils.StringsSigns -private const val CARD_NUMBER_SART_DIGITS_COUNT = 12 -private const val DATE_PART_LENGTH = 2 -private const val CVV_LENGTH = 3 - internal class TangemPayCardDetailsBlockStateFactory( private val cardNumberEnd: String, private val onReveal: () -> Unit, private val onCopy: (String) -> Unit, ) { - private val cardStartMasked = maskedBlock(CARD_NUMBER_SART_DIGITS_COUNT) - private val dateMasked = maskedBlock(DATE_PART_LENGTH) - private val cvvMasked = maskedBlock(CVV_LENGTH) - fun getInitialState() = TangemPayCardDetailsUM( - number = CardDetailsFormatUtil.formatCardNumber(cardNumber = "$cardStartMasked$cardNumberEnd"), - expiry = CardDetailsFormatUtil.formatDate(month = dateMasked, year = dateMasked), - cvv = cvvMasked, - buttonText = TextReference.Res(R.string.tangempay_card_details_reveal_text), + number = "${StringsSigns.ASTERISK}$cardNumberEnd", + expiry = "", + cvv = "", + buttonText = resourceReference(R.string.tangempay_card_details_reveal_text), onClick = onReveal, onCopy = onCopy, isHidden = true, + cardFrozenState = TangemPayCardFrozenState.Unfrozen, ) - - private fun maskedBlock(count: Int) = StringsSigns.DOT.repeat(count) } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt index 0acd6d5f66..1b882ec4a5 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt @@ -6,20 +6,25 @@ import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.themedColor import com.tangem.core.ui.res.TangemTheme +import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.features.tangempay.details.impl.R +import com.tangem.features.tangempay.model.transformers.TangemPayCardFrozenStateConverter import com.tangem.features.tangempay.utils.TangemPayDetailIntents import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList @Suppress("LongParameterList") internal class TangemPayDetailsStateFactory( private val onBack: () -> Unit, private val intents: TangemPayDetailIntents, - private val isCardFrozen: Boolean, + private val cardFrozenState: TangemPayCardFrozenState, + private val converter: TangemPayCardFrozenStateConverter, ) { fun getInitialState(): TangemPayDetailsUM { - val cardFrozenStateItem = if (isCardFrozen) { - TangemPayDetailsTopBarMenuItem( + val cardFrozenStateItem = when (cardFrozenState) { + is TangemPayCardFrozenState.Pending -> null + is TangemPayCardFrozenState.Frozen -> TangemPayDetailsTopBarMenuItem( type = TangemPayDetailsTopBarMenuItemType.UnfreezeCard, dropdownItem = TangemDropdownMenuItem( title = resourceReference(R.string.tangempay_card_details_unfreeze_card), @@ -27,8 +32,7 @@ internal class TangemPayDetailsStateFactory( onClick = intents::onClickUnfreezeCard, ), ) - } else { - TangemPayDetailsTopBarMenuItem( + is TangemPayCardFrozenState.Unfrozen -> TangemPayDetailsTopBarMenuItem( type = TangemPayDetailsTopBarMenuItemType.FreezeCard, dropdownItem = TangemDropdownMenuItem( title = resourceReference(R.string.tangempay_card_details_freeze_card), @@ -41,7 +45,7 @@ internal class TangemPayDetailsStateFactory( return TangemPayDetailsUM( topBarConfig = TangemPayDetailsTopBarConfig( onBackClick = onBack, - items = persistentListOf( + items = listOfNotNull( TangemPayDetailsTopBarMenuItem( type = TangemPayDetailsTopBarMenuItemType.ChangePin, dropdownItem = TangemDropdownMenuItem( @@ -59,7 +63,7 @@ internal class TangemPayDetailsStateFactory( ), ), cardFrozenStateItem, - ), + ).toPersistentList(), ), pullToRefreshConfig = PullToRefreshConfig( isRefreshing = false, @@ -73,15 +77,11 @@ internal class TangemPayDetailsStateFactory( onClick = intents::onClickAddFunds, ), ), - frozenState = if (isCardFrozen) { - CardFrozenState.Frozen(intents::onClickUnfreezeCard) - } else { - CardFrozenState.Unfrozen - }, ), addToWalletBlockState = null, isBalanceHidden = false, addFundsEnabled = true, + cardFrozenState = converter.convert(cardFrozenState), ) } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt index 46a09a01fd..3923a92fe7 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt @@ -3,6 +3,7 @@ package com.tangem.features.tangempay.entity import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.visa.model.TangemPayCardFrozenState import kotlinx.collections.immutable.ImmutableList internal data class TangemPayDetailsUM( @@ -12,6 +13,7 @@ internal data class TangemPayDetailsUM( val addToWalletBlockState: AddToWalletBlockState?, val isBalanceHidden: Boolean, val addFundsEnabled: Boolean, + val cardFrozenState: CardFrozenState, ) internal data class TangemPayCardDetailsUM( @@ -23,21 +25,19 @@ internal data class TangemPayCardDetailsUM( val onCopy: (String) -> Unit = {}, val isHidden: Boolean = true, val isLoading: Boolean = false, + val cardFrozenState: TangemPayCardFrozenState, ) internal sealed class TangemPayDetailsBalanceBlockState { abstract val actionButtons: ImmutableList - abstract val frozenState: CardFrozenState data class Loading( override val actionButtons: ImmutableList, - override val frozenState: CardFrozenState, ) : TangemPayDetailsBalanceBlockState() data class Content( override val actionButtons: ImmutableList, - override val frozenState: CardFrozenState, val cryptoBalance: String, val fiatBalance: String, val isBalanceFlickering: Boolean, @@ -45,7 +45,6 @@ internal sealed class TangemPayDetailsBalanceBlockState { data class Error( override val actionButtons: ImmutableList, - override val frozenState: CardFrozenState, ) : TangemPayDetailsBalanceBlockState() } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardDetailsBlockModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardDetailsBlockModel.kt index e4ca16b23d..4582c81540 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardDetailsBlockModel.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayCardDetailsBlockModel.kt @@ -21,12 +21,10 @@ import com.tangem.features.tangempay.model.transformers.DetailsRevealedStateTran import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn -import com.tangem.utils.transformer.update -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject +import com.tangem.utils.transformer.update as transformerUpdate @Stable @ModelScoped @@ -53,6 +51,7 @@ internal class TangemPayCardDetailsBlockModel @Inject constructor( private val revealCardDetailsJobHolder = JobHolder() init { + subscribeToCardFrozenState() modelScope.launch { cardDetailsEventListener.event.collectLatest { event -> when (event) { @@ -63,22 +62,29 @@ internal class TangemPayCardDetailsBlockModel @Inject constructor( } } + private fun subscribeToCardFrozenState() { + cardDetailsRepository + .cardFrozenState(params.params.config.cardId) + .onEach { uiState.update { state -> state.copy(cardFrozenState = it) } } + .launchIn(modelScope) + } + private fun revealCardDetails() { modelScope.launch { - uiState.update( + uiState.transformerUpdate( transformer = DetailsRevealProgressStateTransformer(onClickHide = ::hideCardDetails), ) cardDetailsRepository.revealCardDetails() - .onRight { - uiState.update( + .onRight { cardDetails -> + uiState.transformerUpdate( transformer = DetailsRevealedStateTransformer( - details = it, + details = cardDetails, onClickHide = ::hideCardDetails, ), ) } .onLeft { - uiState.update(transformer = DetailsHiddenStateTransformer(stateFactory)) + uiState.transformerUpdate(transformer = DetailsHiddenStateTransformer(stateFactory)) showError() } }.saveIn(revealCardDetailsJobHolder) @@ -87,7 +93,7 @@ internal class TangemPayCardDetailsBlockModel @Inject constructor( fun hideCardDetails() { modelScope.launch { revealCardDetailsJobHolder.cancel() - uiState.update(transformer = DetailsHiddenStateTransformer(stateFactory)) + uiState.transformerUpdate(transformer = DetailsHiddenStateTransformer(stateFactory)) } } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt index 3cd51e527f..675f2e3db5 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt @@ -20,6 +20,7 @@ import com.tangem.domain.models.TokenReceiveConfig import com.tangem.domain.pay.TangemPayTopUpData import com.tangem.domain.pay.model.TangemPayCardBalance import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository +import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.domain.visa.model.TangemPayTxHistoryItem import com.tangem.features.tangempay.TangemPayConstants import com.tangem.features.tangempay.components.AddFundsListener @@ -42,8 +43,10 @@ import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn import com.tangem.utils.transformer.update import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.flow.update +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 @@ -64,10 +67,12 @@ internal class TangemPayDetailsModel @Inject constructor( private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require() + private val cardFrozenStateConverter = TangemPayCardFrozenStateConverter(onUnfreezeClick = ::onClickUnfreezeCard) private val stateFactory = TangemPayDetailsStateFactory( onBack = router::pop, intents = this, - isCardFrozen = params.config.isCardFrozen, + cardFrozenState = params.config.cardFrozenState, + converter = cardFrozenStateConverter, ) val uiState: StateFlow @@ -85,6 +90,23 @@ internal class TangemPayDetailsModel @Inject constructor( handleBalanceHiding() fetchAddToWalletBanner() fetchBalance() + subscribeToCardFrozenState() + } + + private fun subscribeToCardFrozenState() { + cardDetailsRepository + .cardFrozenState(params.config.cardId) + .onEach { state -> + uiState.update( + TangemPayFreezeUnfreezeStateTransformer( + cardFrozenState = state, + onFreezeClick = ::onClickFreezeCard, + onUnfreezeClick = ::onClickUnfreezeCard, + converter = cardFrozenStateConverter, + ), + ) + } + .launchIn(modelScope) } override fun onClickChangePin() { @@ -101,54 +123,64 @@ internal class TangemPayDetailsModel @Inject constructor( private fun freezeCard() { modelScope.launch { - uiState.update(TangemPayFreezeUnfreezeProcessTransformer) cardDetailsRepository.freezeCard(cardId = params.config.cardId) .onLeft { uiMessageSender.send(SnackbarMessage(resourceReference(R.string.tangem_pay_freeze_card_failed))) - uiState.update( - TangemPayFreezeUnfreezeStateTransformer( - frozen = false, - onFreezeClick = ::onClickFreezeCard, - onUnfreezeClick = ::onClickUnfreezeCard, - ), - ) } - .onRight { - uiMessageSender.send(SnackbarMessage(resourceReference(R.string.tangem_pay_freeze_card_success))) - uiState.update( - TangemPayFreezeUnfreezeStateTransformer( - frozen = true, - onFreezeClick = ::onClickFreezeCard, - onUnfreezeClick = ::onClickUnfreezeCard, - ), - ) + .onRight { state -> + when (state) { + TangemPayCardFrozenState.Frozen -> { + uiMessageSender.send( + SnackbarMessage(resourceReference(R.string.tangem_pay_freeze_card_success)), + ) + uiState.update( + TangemPayFreezeUnfreezeStateTransformer( + cardFrozenState = state, + onFreezeClick = ::onClickFreezeCard, + onUnfreezeClick = ::onClickUnfreezeCard, + converter = cardFrozenStateConverter, + ), + ) + } + TangemPayCardFrozenState.Unfrozen -> { + uiMessageSender.send( + SnackbarMessage(resourceReference(R.string.tangem_pay_freeze_card_failed)), + ) + } + TangemPayCardFrozenState.Pending -> Unit // TODO [REDACTED_JIRA] + } } } } private fun unfreezeCard() { modelScope.launch { - uiState.update(TangemPayFreezeUnfreezeProcessTransformer) cardDetailsRepository.unfreezeCard(cardId = params.config.cardId) .onLeft { uiMessageSender.send(SnackbarMessage(resourceReference(R.string.tangem_pay_unfreeze_card_failed))) - uiState.update( - TangemPayFreezeUnfreezeStateTransformer( - frozen = true, - onFreezeClick = ::onClickFreezeCard, - onUnfreezeClick = ::onClickUnfreezeCard, - ), - ) } - .onRight { - uiMessageSender.send(SnackbarMessage(resourceReference(R.string.tangem_pay_unfreeze_card_success))) - uiState.update( - TangemPayFreezeUnfreezeStateTransformer( - frozen = false, - onFreezeClick = ::onClickFreezeCard, - onUnfreezeClick = ::onClickUnfreezeCard, - ), - ) + .onRight { state -> + when (state) { + TangemPayCardFrozenState.Unfrozen -> { + uiMessageSender.send( + SnackbarMessage(resourceReference(R.string.tangem_pay_unfreeze_card_success)), + ) + uiState.update( + TangemPayFreezeUnfreezeStateTransformer( + cardFrozenState = state, + onFreezeClick = ::onClickFreezeCard, + onUnfreezeClick = ::onClickUnfreezeCard, + converter = cardFrozenStateConverter, + ), + ) + } + TangemPayCardFrozenState.Frozen -> { + uiMessageSender.send( + SnackbarMessage(resourceReference(R.string.tangem_pay_unfreeze_card_failed)), + ) + } + TangemPayCardFrozenState.Pending -> Unit // TODO [REDACTED_JIRA] + } } } } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt index a357c0434d..90d3d95786 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt @@ -18,10 +18,7 @@ internal class DetailsBalanceTransformer( override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM { val balance = when (balance) { is Either.Left -> { - TangemPayDetailsBalanceBlockState.Error( - actionButtons = persistentListOf(), - frozenState = prevState.balanceBlockState.frozenState, - ) + TangemPayDetailsBalanceBlockState.Error(actionButtons = persistentListOf()) } is Either.Right -> { TangemPayDetailsBalanceBlockState.Content( @@ -30,7 +27,6 @@ internal class DetailsBalanceTransformer( // TODO [REDACTED_TASK_KEY]: Add crypto balance when the BFF is ready cryptoBalance = "", actionButtons = prevState.balanceBlockState.actionButtons, - frozenState = prevState.balanceBlockState.frozenState, ) } } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsRevealedStateTransformer.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsRevealedStateTransformer.kt index 654ddaa52b..b4850f2707 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsRevealedStateTransformer.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsRevealedStateTransformer.kt @@ -1,26 +1,39 @@ package com.tangem.features.tangempay.model.transformers -import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.pay.model.TangemPayCardDetails import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM -import com.tangem.features.tangempay.utils.CardDetailsFormatUtil import com.tangem.utils.transformer.Transformer +private const val CARD_NUMBER_CHUNK_LENGTH = 4 +private const val DATE_LENGTH = 2 + internal class DetailsRevealedStateTransformer( private val details: TangemPayCardDetails, private val onClickHide: (() -> Unit), ) : Transformer { override fun transform(prevState: TangemPayCardDetailsUM): TangemPayCardDetailsUM { - return TangemPayCardDetailsUM( - number = CardDetailsFormatUtil.formatCardNumber(cardNumber = details.pan), - expiry = CardDetailsFormatUtil.formatDate(month = details.expirationMonth, year = details.expirationYear), + return prevState.copy( + number = formatCardNumber(cardNumber = details.pan), + expiry = formatDate(month = details.expirationMonth, year = details.expirationYear), cvv = details.cvv, onClick = onClickHide, - buttonText = TextReference.Res(R.string.tangempay_card_details_hide_text), - onCopy = prevState.onCopy, + buttonText = resourceReference(R.string.tangempay_card_details_hide_text), isHidden = false, ) } + + private fun formatCardNumber(cardNumber: String): String { + return cardNumber + .filterNot { it.isWhitespace() } + .chunked(CARD_NUMBER_CHUNK_LENGTH).joinToString(" ") + } + + private fun formatDate(month: String, year: String): String { + val monthFormatted = month.padStart(DATE_LENGTH, '0') + val yearFormatted = year.takeLast(DATE_LENGTH) + return "$monthFormatted/$yearFormatted" + } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayCardFrozenStateConverter.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayCardFrozenStateConverter.kt new file mode 100644 index 0000000000..1be3c5e765 --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayCardFrozenStateConverter.kt @@ -0,0 +1,18 @@ +package com.tangem.features.tangempay.model.transformers + +import com.tangem.domain.visa.model.TangemPayCardFrozenState +import com.tangem.features.tangempay.entity.CardFrozenState +import com.tangem.utils.converter.Converter + +internal class TangemPayCardFrozenStateConverter( + private val onUnfreezeClick: () -> Unit, +) : Converter { + + override fun convert(value: TangemPayCardFrozenState): CardFrozenState { + return when (value) { + TangemPayCardFrozenState.Unfrozen -> CardFrozenState.Unfrozen + TangemPayCardFrozenState.Frozen -> CardFrozenState.Frozen(onUnfreezeClick) + TangemPayCardFrozenState.Pending -> CardFrozenState.Pending + } + } +} \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayFreezeUnfreezeProcessTransformer.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayFreezeUnfreezeProcessTransformer.kt deleted file mode 100644 index 8b696b4b6f..0000000000 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayFreezeUnfreezeProcessTransformer.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.features.tangempay.model.transformers - -import com.tangem.features.tangempay.entity.CardFrozenState -import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState -import com.tangem.features.tangempay.entity.TangemPayDetailsUM -import com.tangem.utils.transformer.Transformer - -internal object TangemPayFreezeUnfreezeProcessTransformer : Transformer { - override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM { - val newBalanceBlockState = when (val balanceState = prevState.balanceBlockState) { - is TangemPayDetailsBalanceBlockState.Error -> balanceState.copy(frozenState = CardFrozenState.Pending) - is TangemPayDetailsBalanceBlockState.Loading -> balanceState.copy(frozenState = CardFrozenState.Pending) - is TangemPayDetailsBalanceBlockState.Content -> balanceState.copy( - isBalanceFlickering = true, - frozenState = CardFrozenState.Pending, - ) - } - - return prevState.copy(balanceBlockState = newBalanceBlockState) - } -} \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayFreezeUnfreezeStateTransformer.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayFreezeUnfreezeStateTransformer.kt index c1a0d6ca7a..eb4dc8c81e 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayFreezeUnfreezeStateTransformer.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayFreezeUnfreezeStateTransformer.kt @@ -4,31 +4,39 @@ import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.themedColor import com.tangem.core.ui.res.TangemTheme +import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.features.tangempay.details.impl.R -import com.tangem.features.tangempay.entity.* +import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarMenuItem +import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarMenuItemType.FreezeCard +import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarMenuItemType.UnfreezeCard +import com.tangem.features.tangempay.entity.TangemPayDetailsUM import com.tangem.utils.transformer.Transformer import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toPersistentList internal class TangemPayFreezeUnfreezeStateTransformer( - private val frozen: Boolean, + private val cardFrozenState: TangemPayCardFrozenState, private val onFreezeClick: () -> Unit, private val onUnfreezeClick: () -> Unit, + private val converter: TangemPayCardFrozenStateConverter, ) : Transformer { - private val cardFrozenState = if (frozen) CardFrozenState.Frozen(onUnfreezeClick) else CardFrozenState.Unfrozen - private val removeType = - if (frozen) TangemPayDetailsTopBarMenuItemType.FreezeCard else TangemPayDetailsTopBarMenuItemType.UnfreezeCard - private val addType = - if (frozen) TangemPayDetailsTopBarMenuItemType.UnfreezeCard else TangemPayDetailsTopBarMenuItemType.FreezeCard + private val isCardFrozen: Boolean = when (cardFrozenState) { + is TangemPayCardFrozenState.Frozen -> true + is TangemPayCardFrozenState.Unfrozen, is TangemPayCardFrozenState.Pending -> false + } override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM { - val dropdownMenuItems = createUpdatedMenuItems(prevState.topBarConfig.items) - val newBalanceBlockState = createUpdatedBalanceState(prevState.balanceBlockState) + val dropdownMenuItems = when (cardFrozenState) { + TangemPayCardFrozenState.Frozen, + TangemPayCardFrozenState.Unfrozen, + -> createUpdatedMenuItems(prevState.topBarConfig.items) + TangemPayCardFrozenState.Pending -> prevState.topBarConfig.items + } return prevState.copy( topBarConfig = prevState.topBarConfig.copy(items = dropdownMenuItems), - balanceBlockState = newBalanceBlockState, + cardFrozenState = converter.convert(cardFrozenState), ) } @@ -36,38 +44,25 @@ internal class TangemPayFreezeUnfreezeStateTransformer( items: ImmutableList?, ): ImmutableList? { return items - ?.filter { it.type != removeType } + ?.filterNot { it.type == UnfreezeCard || it.type == FreezeCard } ?.plus(createMenuItemToAdd()) ?.toPersistentList() } private fun createMenuItemToAdd(): TangemPayDetailsTopBarMenuItem { return TangemPayDetailsTopBarMenuItem( - type = addType, + type = if (isCardFrozen) UnfreezeCard else FreezeCard, dropdownItem = TangemDropdownMenuItem( title = resourceReference( - id = if (frozen) { + id = if (isCardFrozen) { R.string.tangempay_card_details_unfreeze_card } else { R.string.tangempay_card_details_freeze_card }, ), textColor = themedColor { TangemTheme.colors.text.primary1 }, - onClick = if (frozen) onUnfreezeClick else onFreezeClick, + onClick = if (isCardFrozen) onUnfreezeClick else onFreezeClick, ), ) } - - private fun createUpdatedBalanceState( - balanceState: TangemPayDetailsBalanceBlockState, - ): TangemPayDetailsBalanceBlockState { - return when (balanceState) { - is TangemPayDetailsBalanceBlockState.Error -> balanceState.copy(frozenState = cardFrozenState) - is TangemPayDetailsBalanceBlockState.Loading -> balanceState.copy(frozenState = cardFrozenState) - is TangemPayDetailsBalanceBlockState.Content -> balanceState.copy( - isBalanceFlickering = false, - frozenState = cardFrozenState, - ) - } - } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/CardDetailsItem.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/CardDetailsItem.kt deleted file mode 100644 index 472b10aab3..0000000000 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/CardDetailsItem.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.features.tangempay.ui - -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.tangem.core.ui.res.TangemTheme -import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM - -@Composable -internal fun CardDetailsItem(state: TangemPayCardDetailsUM, modifier: Modifier = Modifier) { - TangemPayCardDetailsBlock( - modifier = modifier - .padding(top = TangemTheme.dimens.spacing12) - .padding(horizontal = TangemTheme.dimens.spacing16) - .fillMaxWidth(), - state = state, - ) -} \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddToWalletScreen.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddToWalletScreen.kt index 249aee7ace..c8faca06da 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddToWalletScreen.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayAddToWalletScreen.kt @@ -28,6 +28,7 @@ import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.features.tangempay.components.cardDetails.PreviewTangemPayCardDetailsBlockComponent import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetailsBlockComponent import com.tangem.features.tangempay.details.impl.R @@ -64,8 +65,11 @@ internal fun TangemPayAddToWalletScreen( state = listState, contentPadding = PaddingValues(bottom = TangemTheme.dimens.spacing16 + bottomBarHeight), ) { - item(key = TangemPayCardDetailsUM::class.java) { - TangemPayCardDetailsBlockItem(component = cardDetailsBlockComponent, state = cardDetailsState) + item(TangemPayCardDetailsUM::class.java) { + cardDetailsBlockComponent.CardDetailsBlockContent( + modifier = Modifier.padding(horizontal = 16.dp).padding(top = 8.dp), + state = cardDetailsState, + ) } item(key = TangemPayAddToWalletUM::class.java) { @@ -185,6 +189,7 @@ private fun PreviewTangemPayAddToWalletScreen() { onCopy = {}, onClick = {}, buttonText = TextReference.Res(R.string.tangempay_card_details_hide_text), + cardFrozenState = TangemPayCardFrozenState.Unfrozen, ), ), ) diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlock.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlock.kt index 091b6c0731..a564990a6e 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlock.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlock.kt @@ -1,164 +1,306 @@ package com.tangem.features.tangempay.ui -import androidx.compose.animation.core.LinearEasing -import androidx.compose.animation.core.RepeatMode -import androidx.compose.animation.core.animateFloat -import androidx.compose.animation.core.infiniteRepeatable -import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.EaseInOut +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Text +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue 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.graphicsLayer import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.unit.dp -import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.SpacerWMax +import com.tangem.core.ui.components.buttons.common.TangemButtonSize +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.domain.visa.model.TangemPayCardFrozenState import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM -private const val REVEAL_ANIMATION_MILLIS = 500 +@Suppress("MagicNumber") +@Composable +internal fun TangemPayCard(state: TangemPayCardDetailsUM, modifier: Modifier = Modifier) { + val isCardFlipped = !state.isHidden + val animDuration = 900 + val zAxisDistance = 50f // distance between camera and Card + + // rotate Y-axis with animation + val rotateCardY by animateFloatAsState( + targetValue = if (isCardFlipped) 180f else 0f, + animationSpec = tween(durationMillis = animDuration, easing = EaseInOut), + label = "", + ) + + // Determine which side to show based on the rotation angle + val shouldShowDetails = rotateCardY > 90f + + Box( + modifier = modifier + .fillMaxWidth() + .aspectRatio(204f / 130f) // size of img_tangem_pay_visa + .graphicsLayer { + rotationY = rotateCardY + cameraDistance = zAxisDistance + } + .clip(RoundedCornerShape(16.dp)) + .clickable(onClick = state.onClick) + .background(Color(red = 18, green = 21, blue = 31)), + ) { + if (shouldShowDetails) { + TangemPayCardDetailsShownBlock( + cardNumber = state.number, + expiry = state.expiry, + cvv = state.cvv, + onCopyCardNumber = { state.onCopy(state.number) }, + onCopyCvv = { state.onCopy(state.cvv) }, + onCopyExpiry = { state.onCopy(state.expiry) }, + onHideDetails = state.onClick, + modifier = Modifier.graphicsLayer { rotationY = 180f }, + ) + } else { + TangemPayCardDetailsHiddenBlock( + cardFrozenState = state.cardFrozenState, + isLoading = state.isLoading, + shortCardNumber = state.number, + onShowDetails = state.onClick, + ) + } + } +} @Composable -internal fun TangemPayCardDetailsBlock(state: TangemPayCardDetailsUM, modifier: Modifier = Modifier) { - val alpha = if (state.isLoading) { - val infiniteTransition = rememberInfiniteTransition() - val animation by infiniteTransition.animateFloat( - initialValue = 1f, - targetValue = 0.3f, - animationSpec = infiniteRepeatable( - animation = tween(REVEAL_ANIMATION_MILLIS, easing = LinearEasing), - repeatMode = RepeatMode.Reverse, - ), - ) - animation - } else { - 1f - } - Column( - modifier = modifier - .background( - color = TangemTheme.colors.background.primary, - shape = TangemTheme.shapes.roundedCornersMedium, - ), - ) { - CardDetailsBlockHeader(state) - CardDetailsTextContainer( - modifier = Modifier - .graphicsLayer { this.alpha = alpha } - .fillMaxWidth() - .padding(start = 12.dp, end = 12.dp, top = 4.dp), - text = state.number, - onCopy = { state.onCopy(state.number) }, - isHidden = state.isHidden, +private fun TangemPayCardDetailsHiddenBlock( + shortCardNumber: String, + cardFrozenState: TangemPayCardFrozenState, + isLoading: Boolean, + onShowDetails: () -> Unit, + modifier: Modifier = Modifier, +) { + Box(modifier = modifier.fillMaxSize()) { + val imageResId = when (cardFrozenState) { + is TangemPayCardFrozenState.Frozen -> R.drawable.img_frozen_tangem_pay_visa + else -> R.drawable.img_tangem_pay_visa + } + Image( + modifier = Modifier.fillMaxSize(), + painter = painterResource(id = imageResId), + contentDescription = null, ) Row( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(horizontal = 16.dp) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = shortCardNumber, + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.constantWhite, + ) + when (cardFrozenState) { + is TangemPayCardFrozenState.Frozen -> Icon( + modifier = Modifier + .padding(start = 4.dp) + .size(16.dp), + painter = painterResource(id = R.drawable.ic_snow_24), + contentDescription = null, + tint = TangemTheme.colors.icon.constant, + ) + TangemPayCardFrozenState.Pending -> CircularProgressIndicator( + modifier = Modifier + .padding(start = 4.dp) + .size(16.dp), + color = TangemTheme.colors.text.constantWhite, + strokeWidth = 1.dp, + ) + TangemPayCardFrozenState.Unfrozen -> Unit + } + SpacerWMax() + PrimaryButton( + text = stringResourceSafe(id = R.string.tangempay_card_details_show_details), + showProgress = isLoading, + onClick = onShowDetails, + size = TangemButtonSize.Small, + ) + } + } +} + +@Suppress("MagicNumber", "LongParameterList") +@Composable +private fun TangemPayCardDetailsShownBlock( + cardNumber: String, + expiry: String, + cvv: String, + onCopyCardNumber: () -> Unit, + onCopyExpiry: () -> Unit, + onCopyCvv: () -> Unit, + onHideDetails: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .padding(8.dp) + .fillMaxSize(), + ) { + CardDetailsTextContainer( modifier = Modifier .fillMaxWidth() - .padding(start = 12.dp, end = 12.dp, bottom = 12.dp, top = 8.dp), + .weight(1f), + title = stringResourceSafe(R.string.tangempay_card_details_card_number), + text = cardNumber, + onCopy = onCopyCardNumber, + ) + HorizontalDivider(thickness = 1.dp, color = Color(0x1F828282)) // Hardcode since background is an image + Row( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + .padding(top = 8.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), ) { CardDetailsTextContainer( modifier = Modifier - .graphicsLayer { this.alpha = alpha } - .weight(1f), - text = state.expiry, - onCopy = { state.onCopy(state.expiry) }, - isHidden = state.isHidden, + .weight(1f) + .wrapContentHeight(), + title = stringResourceSafe(R.string.tangempay_card_details_expiry), + text = expiry, + onCopy = onCopyExpiry, ) + VerticalDivider(thickness = 1.dp, color = Color(0x1F828282)) // Hardcode since background is an image CardDetailsTextContainer( modifier = Modifier - .graphicsLayer { this.alpha = alpha } - .weight(1f), - text = state.cvv, - onCopy = { state.onCopy(state.cvv) }, - isHidden = state.isHidden, + .weight(1f) + .wrapContentHeight(), + title = stringResourceSafe(R.string.tangempay_card_details_cvc), + text = cvv, + onCopy = onCopyCvv, + ) + } + Row { + SpacerWMax() + PrimaryButton( + text = stringResourceSafe(id = R.string.tangempay_card_details_hide_details), + onClick = onHideDetails, + size = TangemButtonSize.Small, ) } } } @Composable -private fun CardDetailsBlockHeader(state: TangemPayCardDetailsUM, modifier: Modifier = Modifier) { +private fun CardDetailsTextContainer(title: String, text: String, onCopy: () -> Unit, modifier: Modifier = Modifier) { Row( - modifier = modifier - .fillMaxWidth(), + modifier = modifier.padding(12.dp), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { - Text( + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text( + text = title, + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + Text( + text = text, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.constantWhite, + ) + } + IconButton( modifier = Modifier - .padding(start = 12.dp, top = 12.dp), - text = stringResourceSafe(R.string.tangempay_card_details_title), - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.subtitle2, - ) - Text( - modifier = Modifier - .padding(top = 8.dp, end = 4.dp) - .clip(TangemTheme.shapes.roundedCornersMedium) - .clickable(onClick = state.onClick) - .padding(horizontal = 8.dp, vertical = 4.dp), - text = state.buttonText.resolveReference(), - color = TangemTheme.colors.text.accent, - style = TangemTheme.typography.body2, - ) + .padding(end = TangemTheme.dimens.spacing12) + .size(TangemTheme.dimens.size32), + onClick = onCopy, + ) { + Icon( + modifier = Modifier.size(TangemTheme.dimens.size24), + painter = painterResource(id = R.drawable.ic_copy_new_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, + ) + } } } +@Preview(widthDp = 400, heightDp = 700, showBackground = true) @Composable -private fun CardDetailsTextContainer( - text: String, - isHidden: Boolean, - onCopy: () -> Unit, - modifier: Modifier = Modifier, +private fun TangemPayCardDetailsBlockV2Preview( + @PreviewParameter(TangemPayCardDetailsUMProvider::class) state: TangemPayCardDetailsUM, ) { - Row( - modifier = modifier - .heightIn(min = 48.dp) - .background(color = TangemTheme.colors.field.primary, shape = RoundedCornerShape(16.dp)), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - modifier = Modifier.padding(vertical = 4.dp, horizontal = 12.dp), - text = text, - maxLines = 1, - color = if (isHidden) TangemTheme.colors.text.disabled else TangemTheme.colors.text.primary1, - style = TangemTheme.typography.body2, - ) - - if (!isHidden) { - Spacer(modifier = Modifier.weight(1f)) - - IconButton( + TangemThemePreview { + Box(modifier = Modifier.fillMaxSize()) { + TangemPayCard( + state = state, modifier = Modifier - .padding(end = TangemTheme.dimens.spacing8) - .size(TangemTheme.dimens.size32), - onClick = onCopy, - ) { - Icon( - modifier = Modifier.size(TangemTheme.dimens.size24), - painter = painterResource(id = R.drawable.ic_copy_new_24), - tint = TangemTheme.colors.icon.informative, - contentDescription = null, - ) - } + .padding(horizontal = 16.dp) + .padding(top = 12.dp), + ) } } -} \ No newline at end of file +} + +private class TangemPayCardDetailsUMProvider : CollectionPreviewParameterProvider( + collection = listOf( + TangemPayCardDetailsUM( + isLoading = false, + onClick = {}, + number = "*1245", + expiry = "", + cvv = "", + buttonText = resourceReference(R.string.tangempay_card_details_show_details), + onCopy = {}, + isHidden = true, + cardFrozenState = TangemPayCardFrozenState.Frozen, + ), + TangemPayCardDetailsUM( + isLoading = false, + onClick = {}, + number = "*1245", + expiry = "", + cvv = "", + buttonText = resourceReference(R.string.tangempay_card_details_show_details), + onCopy = {}, + isHidden = true, + cardFrozenState = TangemPayCardFrozenState.Unfrozen, + ), + TangemPayCardDetailsUM( + isLoading = false, + onClick = {}, + number = "*1245", + expiry = "", + cvv = "", + buttonText = resourceReference(R.string.tangempay_card_details_show_details), + onCopy = {}, + isHidden = true, + cardFrozenState = TangemPayCardFrozenState.Pending, + ), + TangemPayCardDetailsUM( + isLoading = false, + onClick = {}, + buttonText = resourceReference(R.string.tangempay_card_details_hide_details), + onCopy = {}, + isHidden = false, + number = "1234 5678 9012 3456", + expiry = "12/34", + cvv = "123", + cardFrozenState = TangemPayCardFrozenState.Unfrozen, + ), + ), +) \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlockItem.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlockItem.kt deleted file mode 100644 index 01e3a91379..0000000000 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayCardDetailsBlockItem.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tangem.features.tangempay.ui - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetailsBlockComponent -import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM - -@Composable -internal fun TangemPayCardDetailsBlockItem( - component: TangemPayCardDetailsBlockComponent, - state: TangemPayCardDetailsUM, - modifier: Modifier = Modifier, -) { - component.CardDetailsBlockContent(state = state, modifier = modifier) -} \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt index a9814206d6..d55d82db18 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt @@ -7,11 +7,12 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.Alignment +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource @@ -35,10 +36,10 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.orMaskWithStars import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.test.TokenDetailsTopBarTestTags +import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.features.tangempay.components.cardDetails.PreviewTangemPayCardDetailsBlockComponent import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetailsBlockComponent import com.tangem.features.tangempay.components.txHistory.PreviewTangemPayTxHistoryComponent @@ -48,6 +49,7 @@ import com.tangem.features.tangempay.entity.* import com.tangem.utils.StringsSigns.DASH_SIGN import kotlinx.collections.immutable.persistentListOf +@Suppress("LongMethod") @Composable internal fun TangemPayDetailsScreen( state: TangemPayDetailsUM, @@ -77,12 +79,33 @@ internal fun TangemPayDetailsScreen( bottom = TangemTheme.dimens.spacing16 + bottomBarHeight, ), ) { + item(TangemPayCardDetailsUM::class.java) { + cardDetailsBlockComponent.CardDetailsBlockContent( + modifier = Modifier.padding(horizontal = 16.dp).padding(top = 8.dp), + state = cardDetailsState, + ) + } + when (state.cardFrozenState) { + is CardFrozenState.Frozen -> item(CardFrozenState.Frozen::class.java) { + PrimaryButton( + modifier = Modifier + .animateItem() + .padding(horizontal = 16.dp) + .padding(top = 12.dp) + .fillMaxWidth(), + text = stringResourceSafe(R.string.tangempay_card_details_unfreeze_card), + onClick = state.cardFrozenState.onUnfreeze, + ) + } + else -> Unit + } item( key = TangemPayDetailsBalanceBlockState::class.java, content = { TangemPayDetailsBalanceBlock( modifier = modifier .padding(horizontal = TangemTheme.dimens.spacing16) + .padding(top = 12.dp) .fillMaxWidth(), state = state.balanceBlockState, isBalanceHidden = state.isBalanceHidden, @@ -102,23 +125,6 @@ internal fun TangemPayDetailsScreen( }, ) } - item(TangemPayCardDetailsUM::class.java) { - TangemPayCardDetailsBlockItem(component = cardDetailsBlockComponent, state = cardDetailsState) - } - when (val cardFrozenState = state.balanceBlockState.frozenState) { - is CardFrozenState.Frozen -> item(CardFrozenState.Frozen::class.java) { - PrimaryButton( - modifier = Modifier - .animateItem() - .padding(horizontal = 16.dp) - .padding(top = 12.dp) - .fillMaxWidth(), - text = stringResourceSafe(R.string.tangempay_card_details_unfreeze_card), - onClick = cardFrozenState.onUnfreeze, - ) - } - else -> Unit - } with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryState) } } } @@ -140,20 +146,12 @@ internal fun TangemPayDetailsBalanceBlock( ) .padding(vertical = 12.dp), ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 12.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = stringResourceSafe(R.string.tangempay_title), - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.subtitle2, - ) - TangemPayCardStatus(state = state.frozenState) - } + Text( + modifier = Modifier.padding(start = 12.dp), + text = stringResourceSafe(R.string.tangempay_title), + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.subtitle2, + ) FiatBalance( modifier = Modifier.padding(start = 12.dp, top = 8.dp), state = state, @@ -176,36 +174,6 @@ internal fun TangemPayDetailsBalanceBlock( } } -@Composable -private fun TangemPayCardStatus(state: CardFrozenState, modifier: Modifier = Modifier) { - when (state) { - is CardFrozenState.Unfrozen -> Unit - is CardFrozenState.Pending -> CircularProgressIndicator( - modifier = modifier.size(18.dp), - trackColor = Color.Transparent, - color = TangemColorPalette.Azure, - strokeWidth = 2.dp, - ) - is CardFrozenState.Frozen -> Row( - modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(4.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - painter = painterResource(id = R.drawable.ic_snow_24), - tint = TangemTheme.colors.text.disabled, - contentDescription = null, - modifier = Modifier.size(16.dp), - ) - Text( - text = stringResourceSafe(R.string.tangem_pay_card_frozen), - color = TangemTheme.colors.text.disabled, - style = TangemTheme.typography.body2, - ) - } - } -} - @Composable private fun FiatBalance( state: TangemPayDetailsBalanceBlockState, @@ -224,30 +192,18 @@ private fun FiatBalance( text = state.fiatBalance.orMaskWithStars(isBalanceHidden), style = TangemTheme.typography.h2.applyBladeBrush( isEnabled = state.isBalanceFlickering, - textColor = getTextColor( - isCardFrozen = state.frozenState is CardFrozenState.Frozen, - defaultColor = TangemTheme.colors.text.primary1, - ), + textColor = TangemTheme.colors.text.primary1, ), ) is TangemPayDetailsBalanceBlockState.Error -> Text( modifier = modifier, text = DASH_SIGN.orMaskWithStars(isBalanceHidden), style = TangemTheme.typography.h2, - color = getTextColor( - isCardFrozen = state.frozenState is CardFrozenState.Frozen, - defaultColor = TangemTheme.colors.text.primary1, - ), + color = TangemTheme.colors.text.primary1, ) } } -@ReadOnlyComposable -@Composable -private fun getTextColor(isCardFrozen: Boolean, defaultColor: Color): Color { - return if (isCardFrozen) TangemTheme.colors.text.tertiary else defaultColor -} - @Suppress("UnusedPrivateMember") @Composable private fun CryptoBalance( @@ -351,6 +307,7 @@ private fun TangemPayDetailsScreenPreview( onCopy = {}, onClick = {}, buttonText = TextReference.Res(R.string.tangempay_card_details_hide_text), + cardFrozenState = TangemPayCardFrozenState.Unfrozen, ), ), ) @@ -373,22 +330,20 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt index 3ae2ad80f0..8b2b8cc9b5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt @@ -9,6 +9,7 @@ import com.tangem.domain.pay.model.CustomerInfo.ProductInstance import com.tangem.domain.pay.model.MainScreenCustomerInfo import com.tangem.domain.pay.model.OrderStatus.CANCELED import com.tangem.domain.pay.model.OrderStatus.UNKNOWN +import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueAvailableState @@ -24,6 +25,7 @@ private const val POLYGON_CHAIN_ID = 137 internal class TangemPayInitialStateTransformer( private val value: MainScreenCustomerInfo? = null, + private val cardFrozenState: TangemPayCardFrozenState, private val onClickIssue: () -> Unit = {}, private val onClickKyc: () -> Unit = {}, private val openDetails: (config: TangemPayDetailsConfig) -> Unit = {}, @@ -54,10 +56,7 @@ internal class TangemPayInitialStateTransformer( openDetails( TangemPayDetailsConfig( cardId = productInstance.cardId, - isCardFrozen = when (productInstance.status) { - ProductInstance.Status.ACTIVE -> false - ProductInstance.Status.INACTIVE -> true - }, + cardFrozenState = cardFrozenState, customerWalletAddress = cardInfo.customerWalletAddress, cardNumberEnd = cardInfo.lastFourDigits, chainId = POLYGON_CHAIN_ID,