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 f94eb30195..8bbd95fb92 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 @@ -11,6 +11,7 @@ data class TangemPayDetailsConfig( val isPinSet: Boolean, val cardFrozenState: TangemPayCardFrozenState, val cardNumberEnd: String, + val isReissuing: Boolean, val chainId: Int, val isTangemPayDeactivated: Boolean, val displayName: CardDisplayName?, diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/DefaultTangemPayDetailsContainerComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/DefaultTangemPayDetailsContainerComponent.kt index 94de672f75..885bf7e9bc 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/DefaultTangemPayDetailsContainerComponent.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/DefaultTangemPayDetailsContainerComponent.kt @@ -65,9 +65,9 @@ internal class DefaultTangemPayDetailsContainerComponent @AssistedInject constru tokenReceiveComponentFactory = tokenReceiveComponentFactory, expressTransactionsComponentProvider = expressTransactionsComponentProvider, ) - TangemPayAccountDetailsInnerRoute.CardDetails -> tangemPayCardPageFactory.create( + is TangemPayAccountDetailsInnerRoute.CardDetails -> tangemPayCardPageFactory.create( context = childByContext(componentContext = componentContext, router = innerRouter), - params = TangemPayCardPageComponent.Params(userWalletId = params.userWalletId, config = params.config), + params = TangemPayCardPageComponent.Params(userWalletId = params.userWalletId, config = config.config), ) TangemPayAccountDetailsInnerRoute.AddToWallet -> TangemPayAddToWalletComponent( appComponentContext = childByContext(componentContext = componentContext, router = innerRouter), 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 2f630968ac..f0cddecb45 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 @@ -21,7 +21,11 @@ internal class TangemPayDetailsStateFactory( private val cardFrozenState: TangemPayCardFrozenState, ) { @Suppress("LongMethod") - fun getInitialState(isTangemPayDeactivated: Boolean, cardNumberEnd: String): TangemPayDetailsUM { + fun getInitialState( + isTangemPayDeactivated: Boolean, + cardNumberEnd: String, + isReissuing: Boolean, + ): TangemPayDetailsUM { return TangemPayDetailsUM( topBarConfig = TangemPayDetailsTopBarConfig( onBackClick = onBack, @@ -51,7 +55,8 @@ internal class TangemPayDetailsStateFactory( cards = persistentListOf( TangemPayDetailsBalanceBlockState.Card( lastDigits = cardNumberEnd, - onClick = intents::onCardClick, + onClick = {}, + isReissuing = isReissuing, ), ), onAddCardClick = intents::onAddCardClick, 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 2a192392ec..a86c20d4d4 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 @@ -84,7 +84,11 @@ internal sealed class TangemPayDetailsBalanceBlockState { ) : TangemPayDetailsBalanceBlockState() data class CardsBlockState(val cards: ImmutableList, val onAddCardClick: () -> Unit) - data class Card(val lastDigits: String, val onClick: () -> Unit) + data class Card( + val lastDigits: String, + val onClick: () -> Unit, + val isReissuing: Boolean, + ) } internal data class AddToWalletBlockState( 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 dded4437b7..9f2a9aa630 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 @@ -19,10 +19,14 @@ import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.feedback.models.WalletMetaInfo +import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TokenReceiveConfig +import com.tangem.domain.models.account.PaymentAccountStatusValue import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory +import com.tangem.domain.pay.TangemPayDetailsConfig +import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier import com.tangem.domain.pay.model.TangemPayCardBalance import com.tangem.domain.pay.model.TangemPayTopUpData import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository @@ -53,10 +57,7 @@ import com.tangem.utils.coroutines.saveIn import com.tangem.utils.logging.TangemLogger import com.tangem.utils.transformer.update import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject @@ -65,6 +66,7 @@ import javax.inject.Inject @ModelScoped internal class TangemPayDetailsModel @Inject constructor( paramsContainer: ParamsContainer, + paymentAccountStatusSupplier: PaymentAccountStatusSupplier, override val dispatchers: CoroutineDispatcherProvider, private val analytics: AnalyticsEventHandler, private val router: Router, @@ -95,6 +97,7 @@ internal class TangemPayDetailsModel @Inject constructor( stateFactory.getInitialState( isTangemPayDeactivated = params.config.isTangemPayDeactivated, cardNumberEnd = params.config.cardNumberEnd, + isReissuing = params.config.isReissuing, ), ) @@ -118,6 +121,21 @@ internal class TangemPayDetailsModel @Inject constructor( if (!params.config.isTangemPayDeactivated) { subscribeToCardFrozenState() fetchAddToWalletBanner() + + paymentAccountStatusSupplier.invoke(params.userWalletId) + .map { it.value } + .filterIsInstance() + .filter { it.source == StatusSource.ACTUAL } + .onEach { state -> + val card = state.cards.firstOrNull() ?: return@onEach + uiState.update( + TangemPayCardDataTransformer( + card = card, + onCardClick = { onCardClick(params.config.copy(cardId = card.id)) }, + ), + ) + } + .launchIn(modelScope) } } @@ -363,9 +381,9 @@ internal class TangemPayDetailsModel @Inject constructor( urlOpener.openUrl(TangemPayConstants.TERMS_AND_LIMITS_LINK) } - override fun onCardClick() { + override fun onCardClick(config: TangemPayDetailsConfig) { analytics.send(TangemPayAnalyticsEvents.CardIconClicked()) - router.push(TangemPayAccountDetailsInnerRoute.CardDetails) + router.push(TangemPayAccountDetailsInnerRoute.CardDetails(config)) } override fun onAddCardClick() { diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayCardDataTransformer.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayCardDataTransformer.kt new file mode 100644 index 0000000000..cd15656cc3 --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/TangemPayCardDataTransformer.kt @@ -0,0 +1,30 @@ +package com.tangem.features.tangempay.model.transformers + +import com.tangem.domain.models.pay.TangemPayCard +import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState +import com.tangem.features.tangempay.entity.TangemPayDetailsUM +import com.tangem.utils.transformer.Transformer +import kotlinx.collections.immutable.persistentListOf + +internal class TangemPayCardDataTransformer( + private val card: TangemPayCard, + private val onCardClick: () -> Unit, +) : Transformer { + + override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM { + val updatedCard = TangemPayDetailsBalanceBlockState.Card( + lastDigits = card.lastDigits, + onClick = onCardClick, + isReissuing = card.isReissuing, + ) + val cardsBlockState = prevState.balanceBlockState.cardsBlockState.copy( + cards = persistentListOf(updatedCard), + ) + val newBalanceBlockState = when (val bs = prevState.balanceBlockState) { + is TangemPayDetailsBalanceBlockState.Loading -> bs.copy(cardsBlockState = cardsBlockState) + is TangemPayDetailsBalanceBlockState.Content -> bs.copy(cardsBlockState = cardsBlockState) + is TangemPayDetailsBalanceBlockState.Error -> bs.copy(cardsBlockState = cardsBlockState) + } + 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/navigation/TangemPayAccountDetailsInnerRoute.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/navigation/TangemPayAccountDetailsInnerRoute.kt index 8ef5c5e9a3..b49fa0a643 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/navigation/TangemPayAccountDetailsInnerRoute.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/navigation/TangemPayAccountDetailsInnerRoute.kt @@ -1,6 +1,7 @@ package com.tangem.features.tangempay.navigation import com.tangem.core.decompose.navigation.Route +import com.tangem.domain.pay.TangemPayDetailsConfig import kotlinx.serialization.Serializable @Serializable @@ -9,7 +10,7 @@ internal sealed class TangemPayAccountDetailsInnerRoute : Route { data object AccountDetails : TangemPayAccountDetailsInnerRoute() @Serializable - data object CardDetails : TangemPayAccountDetailsInnerRoute() + data class CardDetails(val config: TangemPayDetailsConfig) : TangemPayAccountDetailsInnerRoute() @Serializable data object AddToWallet : TangemPayAccountDetailsInnerRoute() 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 fabad9ef58..7044edc626 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 @@ -31,6 +31,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.ui.util.fastAny import androidx.compose.ui.util.fastForEach import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.ui.components.RectangleShimmer @@ -114,30 +115,45 @@ internal fun TangemPayDetailsScreen( SpacerH12() }, ) - if (state.addToWalletBlockState != null) { + + if (state.balanceBlockState.cardsBlockState.cards.fastAny { it.isReissuing }) { item( - key = AddToWalletBlockState::class.java, + key = "REISSUE_MESSAGE", content = { - TangemPayAddToWalletBlock( - state = state.addToWalletBlockState, - modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16), - ) - }, - ) - } - if (state.accountDeactivatedNotificationConfig != null) { - item( - key = "DEACTIVATION_MESSAGE", - content = { - Notification( - modifier = modifier + TangemPayReplacingCardBlock( + modifier = Modifier .padding(horizontal = TangemTheme.dimens.spacing16) .fillMaxWidth(), - config = state.accountDeactivatedNotificationConfig, ) SpacerH12() }, ) + } else { + if (state.addToWalletBlockState != null) { + item( + key = AddToWalletBlockState::class.java, + content = { + TangemPayAddToWalletBlock( + state = state.addToWalletBlockState, + modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16), + ) + }, + ) + } + if (state.accountDeactivatedNotificationConfig != null) { + item( + key = "DEACTIVATION_MESSAGE", + content = { + Notification( + modifier = modifier + .padding(horizontal = TangemTheme.dimens.spacing16) + .fillMaxWidth(), + config = state.accountDeactivatedNotificationConfig, + ) + SpacerH12() + }, + ) + } } if (state.accountDeactivatedNotificationConfig == null) { with(expressTransactionsComponent) { @@ -289,14 +305,26 @@ private fun TangemPayCardItem(card: TangemPayDetailsBalanceBlockState.Card, modi painter = painterResource(R.drawable.img_visa_card_48x32), contentDescription = null, ) - Text( - modifier = Modifier - .align(Alignment.BottomStart) - .padding(4.dp, bottom = 2.dp), - text = card.lastDigits, - style = TangemTheme.typography.overline.copy(letterSpacing = 0.sp), - color = TangemTheme.colors.text.constantWhite, - ) + if (card.isReissuing) { + Icon( + modifier = Modifier + .size(16.dp) + .align(Alignment.BottomStart) + .padding(2.dp), + painter = painterResource(R.drawable.ic_update_32), + contentDescription = null, + tint = TangemTheme.colors.text.constantWhite, + ) + } else { + Text( + modifier = Modifier + .align(Alignment.BottomStart) + .padding(4.dp, bottom = 2.dp), + text = card.lastDigits, + style = TangemTheme.typography.overline.copy(letterSpacing = 0.sp), + color = TangemTheme.colors.text.constantWhite, + ) + } } } @@ -421,8 +449,8 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider