Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-10 13:11:37 +05:00
parent 3f56f32dd6
commit 60911ceb8c
5 changed files with 18 additions and 157 deletions

View file

@ -120,7 +120,6 @@ internal class TangemPayDetailsModel @Inject constructor(
private val refreshStateJobHolder = JobHolder()
private val addToWalletBannerJobHolder = JobHolder()
private val frozenStateJobHolder = JobHolder()
val bottomSheetNavigation: SlotNavigation<TangemPayDetailsNavigation> = SlotNavigation()
@ -141,9 +140,6 @@ internal class TangemPayDetailsModel @Inject constructor(
fetchAddToWalletBanner()
uiState.update { stateFactory.getLoadedState(state) }
uiState.update(DetailsBalanceTransformer(state.balance.fiatBalance))
state.cards.firstOrNull()?.let { card ->
subscribeToCardFrozenState(card.id)
}
}
else -> uiState.update { stateFactory.getLoadingState() }
}
@ -165,22 +161,6 @@ internal class TangemPayDetailsModel @Inject constructor(
fun isRedesignEnabled(): Boolean = tangemPayFeatureToggles.isRedesignEnabled
private fun subscribeToCardFrozenState(cardId: String) {
frozenStateJobHolder.cancel()
cardDetailsRepository
.cardFrozenState(cardId)
.onEach { frozenState ->
uiState.update(
TangemPayFreezeUnfreezeStateTransformer(
cardFrozenState = frozenState,
isDataFresh = currentStatus.value.ifLoadedOrNull { it.isFresh } == true,
),
)
}
.launchIn(modelScope)
.saveIn(frozenStateJobHolder)
}
override fun onClickAddFunds() {
analytics.send(TangemPayAnalyticsEvents.AddFundsClicked())
val balance = currentStatus.value.balanceOrNull()

View file

@ -1,26 +0,0 @@
package com.tangem.features.tangempay.model.transformers
import com.tangem.domain.models.pay.TangemPayCardFrozenState
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.toPersistentList
internal class TangemPayFreezeUnfreezeStateTransformer(
private val cardFrozenState: TangemPayCardFrozenState,
private val isDataFresh: Boolean,
) : Transformer<TangemPayDetailsUM> {
override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM {
val balanceBlockState = if (prevState.balanceBlockState is TangemPayDetailsBalanceBlockState.Content) {
val isEnabled = isDataFresh && cardFrozenState == TangemPayCardFrozenState.Unfrozen
val actionButtons = prevState.balanceBlockState.actionButtons.map {
it.copy(isEnabled = isEnabled)
}
prevState.balanceBlockState.copy(actionButtons = actionButtons.toPersistentList())
} else {
prevState.balanceBlockState
}
return prevState.copy(balanceBlockState = balanceBlockState)
}
}

View file

@ -22,14 +22,12 @@ internal val AccountStatus.Payment.isDeactivated: Boolean
get() = value is PaymentAccountStatusValue.Deactivated
internal val PaymentAccountStatusValue.Loaded.isFresh: Boolean
get() = source == StatusSource.ACTUAL && error == null
get() = source.isActual() && error == null
internal fun AccountStatus.Payment.requireLoaded(): PaymentAccountStatusValue.Loaded =
value as? PaymentAccountStatusValue.Loaded
?: error("Card-detail subflow requires Loaded status, got ${value::class.simpleName}")
internal fun AccountStatus.Payment.firstCard(): TangemPayCard = requireLoaded().cards.first()
internal inline fun <T> AccountStatus.Payment.ifLoadedOrNull(call: (PaymentAccountStatusValue.Loaded) -> T): T? {
val value = value
return if (value is PaymentAccountStatusValue.Loaded) {

View file

@ -96,6 +96,23 @@ internal class TangemPayDetailsStateFactoryTest {
assertThat(state.balanceBlockState.cardsBlockState?.isAddCardEnabled).isTrue()
}
@Test
fun `GIVEN actual status with frozen first card but unfrozen second card WHEN getLoadedState THEN action buttons enabled`() {
val factory = createFactory()
val frozenFirstCard = activeUnfrozenCard.copy(id = "card_frozen", frozenState = TangemPayCardFrozenState.Frozen)
val status = loadedStatus(
statusSource = StatusSource.ACTUAL,
statusError = null,
statusCards = listOf(frozenFirstCard, activeUnfrozenCard),
)
// Act
val state = factory.getLoadedState(status)
// Assert
assertThat(state.balanceBlockState.actionButtons.map { it.isEnabled }).containsExactly(true, true)
}
@Test
fun `GIVEN actual status with issuing card WHEN getLoadedState THEN add card disabled`() {
// Arrange

View file

@ -1,108 +0,0 @@
package com.tangem.features.tangempay.model.transformers
import com.google.common.truth.Truth.assertThat
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.models.pay.TangemPayCardFrozenState
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarConfig
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
import kotlinx.collections.immutable.persistentListOf
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
internal class TangemPayFreezeUnfreezeStateTransformerTest {
@ParameterizedTest
@MethodSource("provideCases")
fun `GIVEN content state WHEN transform THEN buttons enabled only when fresh and unfrozen`(case: Case) {
// Arrange
val state = contentState(actionButtonsEnabled = true)
// Act
val result = TangemPayFreezeUnfreezeStateTransformer(
cardFrozenState = case.frozenState,
isDataFresh = case.isDataFresh,
).transform(state)
// Assert
val enabled = result.balanceBlockState.actionButtons.map { it.isEnabled }
assertThat(enabled).containsExactly(case.expectedEnabled, case.expectedEnabled)
}
@Test
fun `GIVEN non-content state WHEN transform THEN state is unchanged`() {
// Arrange
val state = loadingState()
// Act
val result = TangemPayFreezeUnfreezeStateTransformer(
cardFrozenState = TangemPayCardFrozenState.Unfrozen,
isDataFresh = true,
).transform(state)
// Assert
assertThat(result).isEqualTo(state)
}
private fun contentState(actionButtonsEnabled: Boolean): TangemPayDetailsUM = baseState(
balanceBlockState = TangemPayDetailsBalanceBlockState.Content(
actionButtons = persistentListOf(
actionButton(isEnabled = actionButtonsEnabled),
actionButton(isEnabled = actionButtonsEnabled),
),
cardsBlockState = null,
fiatBalance = TextReference.EMPTY,
isBalanceFlickering = false,
),
)
private fun loadingState(): TangemPayDetailsUM = baseState(
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(
actionButtons = persistentListOf(actionButton(isEnabled = true)),
cardsBlockState = null,
),
)
private fun baseState(balanceBlockState: TangemPayDetailsBalanceBlockState) = TangemPayDetailsUM(
topBarConfig = TangemPayDetailsTopBarConfig(
onBackClick = {},
onOpenMenu = {},
items = persistentListOf(),
itemsV2 = persistentListOf(),
),
pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = {}),
balanceBlockState = balanceBlockState,
addToWalletBlockState = null,
isBalanceHidden = false,
errorNotificationConfig = null,
accountDeactivatedNotificationConfig = null,
)
private fun actionButton(isEnabled: Boolean) = ActionButtonConfig(
text = TextReference.EMPTY,
iconResId = 0,
onClick = {},
isEnabled = isEnabled,
)
internal data class Case(
val frozenState: TangemPayCardFrozenState,
val isDataFresh: Boolean,
val expectedEnabled: Boolean,
)
private companion object {
@JvmStatic
fun provideCases() = listOf(
Case(frozenState = TangemPayCardFrozenState.Unfrozen, isDataFresh = true, expectedEnabled = true),
Case(frozenState = TangemPayCardFrozenState.Unfrozen, isDataFresh = false, expectedEnabled = false),
Case(frozenState = TangemPayCardFrozenState.Frozen, isDataFresh = true, expectedEnabled = false),
Case(frozenState = TangemPayCardFrozenState.Pending, isDataFresh = true, expectedEnabled = false),
Case(frozenState = TangemPayCardFrozenState.Frozen, isDataFresh = false, expectedEnabled = false),
Case(frozenState = TangemPayCardFrozenState.Pending, isDataFresh = false, expectedEnabled = false),
)
}
}