Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-17 09:57:59 +04:00
parent 84198eb103
commit b4d11538ea
57 changed files with 361 additions and 4136 deletions

View file

@ -1,7 +1,6 @@
package com.tangem.features.tangempay package com.tangem.features.tangempay
interface TangemPayFeatureToggles { interface TangemPayFeatureToggles {
val isRedesignEnabled: Boolean
val isRemoveAccountEnabled: Boolean val isRemoveAccountEnabled: Boolean
val isTiersPlusPlanEnabled: Boolean val isTiersPlusPlanEnabled: Boolean
val isCashbackEnabled: Boolean val isCashbackEnabled: Boolean

View file

@ -6,9 +6,6 @@ import com.tangem.core.configtoggle.feature.FeatureTogglesManager
internal class DefaultTangemPayFeatureToggles( internal class DefaultTangemPayFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager, private val featureTogglesManager: FeatureTogglesManager,
) : TangemPayFeatureToggles { ) : TangemPayFeatureToggles {
override val isRedesignEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.APP_REDESIGN_ENABLED)
override val isRemoveAccountEnabled: Boolean override val isRemoveAccountEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.AND_15741_VISA_PAY_REMOVE_ACCOUNT) get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.AND_15741_VISA_PAY_REMOVE_ACCOUNT)

View file

@ -9,7 +9,6 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.TangemPayTopUpData import com.tangem.domain.pay.model.TangemPayTopUpData
import com.tangem.features.tangempay.model.TangemPayAddFundsModel import com.tangem.features.tangempay.model.TangemPayAddFundsModel
import com.tangem.features.tangempay.ui.TangemPayAddFundsContent
import com.tangem.features.tangempay.ui.TangemPayAddFundsContentV2 import com.tangem.features.tangempay.ui.TangemPayAddFundsContentV2
import java.math.BigDecimal import java.math.BigDecimal
@ -26,11 +25,7 @@ internal class TangemPayAddFundsComponent(
@Composable @Composable
override fun BottomSheet() { override fun BottomSheet() {
if (model.isRedesignEnabled()) { TangemPayAddFundsContentV2(state = model.uiState)
TangemPayAddFundsContentV2(state = model.uiState)
} else {
TangemPayAddFundsContent(state = model.uiState)
}
} }
data class Params( data class Params(

View file

@ -2,18 +2,15 @@ package com.tangem.features.tangempay.components
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.res.LocalVisaRedesignEnabled
import com.tangem.domain.models.pay.TangemPayCard import com.tangem.domain.models.pay.TangemPayCard
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.tangempay.model.TangemPayAddToWalletModel import com.tangem.features.tangempay.model.TangemPayAddToWalletModel
import com.tangem.features.tangempay.ui.TangemPayAddToWalletScreen
import com.tangem.features.tangempay.ui.TangemPayAddToWalletScreenV2 import com.tangem.features.tangempay.ui.TangemPayAddToWalletScreenV2
internal class TangemPayAddToWalletComponent( internal class TangemPayAddToWalletComponent(
@ -28,19 +25,10 @@ internal class TangemPayAddToWalletComponent(
val state by model.uiState.collectAsStateWithLifecycle() val state by model.uiState.collectAsStateWithLifecycle()
val cardDetailsState by model.cardDetailsState.collectAsStateWithLifecycle() val cardDetailsState by model.cardDetailsState.collectAsStateWithLifecycle()
BackHandler(onBack = router::pop) BackHandler(onBack = router::pop)
CompositionLocalProvider(LocalVisaRedesignEnabled provides model.isRedesignEnabled()) { TangemPayAddToWalletScreenV2(
if (model.isRedesignEnabled()) { state = state,
TangemPayAddToWalletScreenV2( cardDetailsState = cardDetailsState,
state = state, )
cardDetailsState = cardDetailsState,
)
} else {
TangemPayAddToWalletScreen(
state = state,
cardDetailsState = cardDetailsState,
)
}
}
} }
data class Params(val card: TangemPayCard, val userWalletId: UserWalletId) data class Params(val card: TangemPayCard, val userWalletId: UserWalletId)

View file

@ -17,7 +17,6 @@ import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.decompose.navigation.inner.InnerRouter import com.tangem.core.decompose.navigation.inner.InnerRouter
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.models.account.AccountStatus import com.tangem.domain.models.account.AccountStatus
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.limit.setup.TangemPayCardLimitSetupComponent import com.tangem.features.tangempay.limit.setup.TangemPayCardLimitSetupComponent
import com.tangem.features.tangempay.limit.setup.TangemPayCardLimitSetupSuccessComponent import com.tangem.features.tangempay.limit.setup.TangemPayCardLimitSetupSuccessComponent
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
@ -33,7 +32,6 @@ internal class TangemPayCardPageComponent @AssistedInject constructor(
@Assisted private val params: Params, @Assisted private val params: Params,
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory, private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
private val virtualAccountAddFundsComponentFactory: VirtualAccountAddFundsBottomSheetComponent.Factory, private val virtualAccountAddFundsComponentFactory: VirtualAccountAddFundsBottomSheetComponent.Factory,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
) : ComposableContentComponent, AppComponentContext by appComponentContext { ) : ComposableContentComponent, AppComponentContext by appComponentContext {
private val stackNavigation = StackNavigation<TangemPayCardDetailsInnerRoute>() private val stackNavigation = StackNavigation<TangemPayCardDetailsInnerRoute>()
@ -85,7 +83,6 @@ internal class TangemPayCardPageComponent @AssistedInject constructor(
componentContext = componentContext, componentContext = componentContext,
router = innerRouter, router = innerRouter,
), ),
isRedesignEnabled = tangemPayFeatureToggles.isRedesignEnabled,
) )
is TangemPayCardDetailsInnerRoute.AddToWallet -> TangemPayAddToWalletComponent( is TangemPayCardDetailsInnerRoute.AddToWallet -> TangemPayAddToWalletComponent(
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter), appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
@ -110,7 +107,6 @@ internal class TangemPayCardPageComponent @AssistedInject constructor(
) )
TangemPayCardDetailsInnerRoute.LimitSetupSuccess -> TangemPayCardLimitSetupSuccessComponent( TangemPayCardDetailsInnerRoute.LimitSetupSuccess -> TangemPayCardLimitSetupSuccessComponent(
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter), appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
isRedesignEnabled = tangemPayFeatureToggles.isRedesignEnabled,
) )
TangemPayCardDetailsInnerRoute.VirtualAccountDepositSuccess -> TangemPayCardDetailsInnerRoute.VirtualAccountDepositSuccess ->
TangemPayVirtualAccountDepositSuccessComponent( TangemPayVirtualAccountDepositSuccessComponent(

View file

@ -1,7 +1,6 @@
package com.tangem.features.tangempay.components package com.tangem.features.tangempay.components
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@ -15,7 +14,6 @@ import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.res.LocalVisaRedesignEnabled
import com.tangem.features.tangempay.closure.TangemPayCloseCardComponent import com.tangem.features.tangempay.closure.TangemPayCloseCardComponent
import com.tangem.features.tangempay.entity.TangemPayCardNavigation import com.tangem.features.tangempay.entity.TangemPayCardNavigation
import com.tangem.features.tangempay.model.TangemPayCardPageModel import com.tangem.features.tangempay.model.TangemPayCardPageModel
@ -50,17 +48,15 @@ internal class TangemPayCardPageScreenComponent(
val selectedCardId by model.selectedCardIdState.collectAsStateWithLifecycle() val selectedCardId by model.selectedCardIdState.collectAsStateWithLifecycle()
val bottomSheet by bottomSheetSlot.subscribeAsState() val bottomSheet by bottomSheetSlot.subscribeAsState()
CompositionLocalProvider(LocalVisaRedesignEnabled provides model.isRedesignEnabled()) { NavigationBar3ButtonsScrim()
NavigationBar3ButtonsScrim() TangemPayCardPageScreen(
TangemPayCardPageScreen( state = state,
state = state, cardControllers = cardControllers,
cardControllers = cardControllers, selectedCardId = selectedCardId,
selectedCardId = selectedCardId, onCardSelect = model::onCardPageSelected,
onCardSelect = model::onCardPageSelected, modifier = modifier,
modifier = modifier, )
) bottomSheet.child?.instance?.BottomSheet()
bottomSheet.child?.instance?.BottomSheet()
}
} }
@Suppress("LongMethod") @Suppress("LongMethod")

View file

@ -12,7 +12,6 @@ import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
import com.tangem.domain.models.pay.TangemPayCard import com.tangem.domain.models.pay.TangemPayCard
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.tangempay.model.TangemPayChangePinModel import com.tangem.features.tangempay.model.TangemPayChangePinModel
import com.tangem.features.tangempay.ui.TangemPayChangePinScreen
import com.tangem.features.tangempay.ui.TangemPayChangePinScreenV2 import com.tangem.features.tangempay.ui.TangemPayChangePinScreenV2
internal class TangemPayChangePinComponent( internal class TangemPayChangePinComponent(
@ -27,17 +26,10 @@ internal class TangemPayChangePinComponent(
val state by model.uiState.collectAsStateWithLifecycle() val state by model.uiState.collectAsStateWithLifecycle()
BackHandler(onBack = router::pop) BackHandler(onBack = router::pop)
DisableScreenshotsDisposableEffect() DisableScreenshotsDisposableEffect()
if (model.isRedesignEnabled()) { TangemPayChangePinScreenV2(
TangemPayChangePinScreenV2( state = state,
state = state, onBackClick = router::pop,
onBackClick = router::pop, )
)
} else {
TangemPayChangePinScreen(
state = state,
onBackClick = router::pop,
)
}
} }
data class Params(val card: TangemPayCard, val userWalletId: UserWalletId) data class Params(val card: TangemPayCard, val userWalletId: UserWalletId)

View file

@ -6,22 +6,16 @@ import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
import com.tangem.features.tangempay.ui.TangemPayChangePinCodeSuccessScreen
import com.tangem.features.tangempay.ui.TangemPayChangePinCodeSuccessScreenV2 import com.tangem.features.tangempay.ui.TangemPayChangePinCodeSuccessScreenV2
internal class TangemPayChangePinSuccessComponent( internal class TangemPayChangePinSuccessComponent(
private val appComponentContext: AppComponentContext, private val appComponentContext: AppComponentContext,
private val isRedesignEnabled: Boolean,
) : AppComponentContext by appComponentContext, ComposableContentComponent { ) : AppComponentContext by appComponentContext, ComposableContentComponent {
@Composable @Composable
override fun Content(modifier: Modifier) { override fun Content(modifier: Modifier) {
BackHandler(onBack = ::backToDetails) BackHandler(onBack = ::backToDetails)
if (isRedesignEnabled) { TangemPayChangePinCodeSuccessScreenV2(onClose = ::backToDetails)
TangemPayChangePinCodeSuccessScreenV2(onClose = ::backToDetails)
} else {
TangemPayChangePinCodeSuccessScreen(onClick = ::backToDetails)
}
} }
private fun backToDetails() { private fun backToDetails() {

View file

@ -1,7 +1,6 @@
package com.tangem.features.tangempay.components package com.tangem.features.tangempay.components
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -18,13 +17,11 @@ import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.res.LocalVisaRedesignEnabled
import com.tangem.features.promobanners.api.PromoBannersBlockComponent import com.tangem.features.promobanners.api.PromoBannersBlockComponent
import com.tangem.features.tangempay.components.txHistory.DefaultTangemPayTxHistoryComponent import com.tangem.features.tangempay.components.txHistory.DefaultTangemPayTxHistoryComponent
import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryDetailsComponent import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryDetailsComponent
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
import com.tangem.features.tangempay.model.TangemPayDetailsModel import com.tangem.features.tangempay.model.TangemPayDetailsModel
import com.tangem.features.tangempay.ui.TangemPayDetailsScreen
import com.tangem.features.tangempay.ui.TangemPayDetailsScreenV2 import com.tangem.features.tangempay.ui.TangemPayDetailsScreenV2
import com.tangem.features.tangempay.utils.VA_DAILY_DEPOSIT_LIMIT_PLACEHOLDER import com.tangem.features.tangempay.utils.VA_DAILY_DEPOSIT_LIMIT_PLACEHOLDER
import com.tangem.features.tangempay.utils.requireLoaded import com.tangem.features.tangempay.utils.requireLoaded
@ -98,26 +95,15 @@ internal class TangemPayDetailsComponent(
horizontalItemPadding = 16.dp, horizontalItemPadding = 16.dp,
) )
} }
CompositionLocalProvider(LocalVisaRedesignEnabled provides model.isRedesignEnabled()) { NavigationBar3ButtonsScrim()
NavigationBar3ButtonsScrim() TangemPayDetailsScreenV2(
if (LocalVisaRedesignEnabled.current) { state = state,
TangemPayDetailsScreenV2( txHistoryComponent = txHistoryComponent,
state = state, expressTransactionsComponent = expressTransactionsComponent,
txHistoryComponent = txHistoryComponent, promoBannersBlockComponent = promoBannersBlock,
expressTransactionsComponent = expressTransactionsComponent, modifier = modifier,
promoBannersBlockComponent = promoBannersBlock, )
modifier = modifier, bottomSheet.child?.instance?.BottomSheet()
)
} else {
TangemPayDetailsScreen(
state = state,
txHistoryComponent = txHistoryComponent,
expressTransactionsComponent = expressTransactionsComponent,
modifier = modifier,
)
}
bottomSheet.child?.instance?.BottomSheet()
}
} }
@Suppress("LongMethod") @Suppress("LongMethod")

View file

@ -2,19 +2,17 @@ package com.tangem.features.tangempay.components
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.res.LocalVisaRedesignEnabled
import com.tangem.domain.models.pay.TangemPayCard import com.tangem.domain.models.pay.TangemPayCard
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.tangempay.entity.DisplayNameState import com.tangem.features.tangempay.entity.DisplayNameState
import com.tangem.features.tangempay.model.TangemPayEditDisplayNameModel import com.tangem.features.tangempay.model.TangemPayEditDisplayNameModel
import com.tangem.features.tangempay.ui.TangemPayEditDisplayNameScreen import com.tangem.features.tangempay.ui.TangemPayEditDisplayNameScreenV2
internal class TangemPayEditDisplayNameComponent( internal class TangemPayEditDisplayNameComponent(
private val appComponentContext: AppComponentContext, private val appComponentContext: AppComponentContext,
@ -38,14 +36,11 @@ internal class TangemPayEditDisplayNameComponent(
), ),
) )
BackHandler(onBack = state.onDismiss) BackHandler(onBack = state.onDismiss)
CompositionLocalProvider(LocalVisaRedesignEnabled provides model.isRedesignEnabled()) { TangemPayEditDisplayNameScreenV2(
TangemPayEditDisplayNameScreen( state = state,
state = state, cardDetailsState = editingCardDetailsState,
cardDetailsState = editingCardDetailsState, modifier = modifier,
modifier = modifier, )
isRedesignEnabled = model.isRedesignEnabled(),
)
}
} }
data class Params(val card: TangemPayCard, val userWalletId: UserWalletId) data class Params(val card: TangemPayCard, val userWalletId: UserWalletId)

View file

@ -8,7 +8,6 @@ import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.tangempay.model.TangemPayReissueCardModel import com.tangem.features.tangempay.model.TangemPayReissueCardModel
import com.tangem.features.tangempay.ui.TangemPayReissueCardContent
import com.tangem.features.tangempay.ui.TangemPayReissueCardContentV2 import com.tangem.features.tangempay.ui.TangemPayReissueCardContentV2
internal class TangemPayReissueCardComponent( internal class TangemPayReissueCardComponent(
@ -23,11 +22,7 @@ internal class TangemPayReissueCardComponent(
@Composable @Composable
override fun BottomSheet() { override fun BottomSheet() {
val state by model.state.collectAsStateWithLifecycle() val state by model.state.collectAsStateWithLifecycle()
if (model.isRedesignEnabled()) { TangemPayReissueCardContentV2(state)
TangemPayReissueCardContentV2(state)
} else {
TangemPayReissueCardContent(state)
}
} }
data class Params( data class Params(

View file

@ -10,7 +10,6 @@ import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.tangempay.model.TangemPayViewPinModel import com.tangem.features.tangempay.model.TangemPayViewPinModel
import com.tangem.features.tangempay.ui.TangemPayViewPinContent
import com.tangem.features.tangempay.ui.TangemPayViewPinContentV2 import com.tangem.features.tangempay.ui.TangemPayViewPinContentV2
internal class TangemPayViewPinComponent( internal class TangemPayViewPinComponent(
@ -29,11 +28,7 @@ internal class TangemPayViewPinComponent(
val state by model.uiState.collectAsStateWithLifecycle() val state by model.uiState.collectAsStateWithLifecycle()
BackHandler(onBack = ::dismiss) BackHandler(onBack = ::dismiss)
DisableScreenshotsDisposableEffect() DisableScreenshotsDisposableEffect()
if (model.isRedesignEnabled()) { TangemPayViewPinContentV2(state = state)
TangemPayViewPinContentV2(state = state)
} else {
TangemPayViewPinContent(state = state)
}
} }
data class Params( data class Params(

View file

@ -7,7 +7,6 @@ import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
import com.tangem.features.tangempay.model.TangemPayTxHistoryModel import com.tangem.features.tangempay.model.TangemPayTxHistoryModel
import com.tangem.features.tangempay.ui.tangemPayTxHistoryItems
import com.tangem.features.tangempay.ui.tangemPayTxHistoryItemsV2 import com.tangem.features.tangempay.ui.tangemPayTxHistoryItemsV2
import com.tangem.features.tangempay.utils.TangemPayTxHistoryUiActions import com.tangem.features.tangempay.utils.TangemPayTxHistoryUiActions
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -21,11 +20,7 @@ internal class DefaultTangemPayTxHistoryComponent(
override val state: StateFlow<TangemPayTxHistoryUM> = model.uiState override val state: StateFlow<TangemPayTxHistoryUM> = model.uiState
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TangemPayTxHistoryUM) { override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TangemPayTxHistoryUM) {
if (model.isRedesignEnabled()) { tangemPayTxHistoryItemsV2(listState, state)
tangemPayTxHistoryItemsV2(listState, state)
} else {
tangemPayTxHistoryItems(listState, state)
}
} }
data class Params( data class Params(

View file

@ -10,7 +10,7 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayTransactionState import com.tangem.features.tangempay.entity.TangemPayTransactionState
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
import com.tangem.features.tangempay.ui.tangemPayTxHistoryItems import com.tangem.features.tangempay.ui.tangemPayTxHistoryItemsV2
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -19,7 +19,7 @@ internal class PreviewTangemPayTxHistoryComponent(txHistoryUM: TangemPayTxHistor
override val state: StateFlow<TangemPayTxHistoryUM> = MutableStateFlow(txHistoryUM) override val state: StateFlow<TangemPayTxHistoryUM> = MutableStateFlow(txHistoryUM)
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TangemPayTxHistoryUM) { override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TangemPayTxHistoryUM) {
tangemPayTxHistoryItems(listState = listState, state = state) tangemPayTxHistoryItemsV2(listState = listState, state = state)
} }
companion object { companion object {

View file

@ -5,11 +5,8 @@ import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.res.LocalVisaRedesignEnabled
import com.tangem.features.tangempay.components.TangemPayTransactionBottomSheetComponent import com.tangem.features.tangempay.components.TangemPayTransactionBottomSheetComponent
import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUiState
import com.tangem.features.tangempay.model.TangemPayTxHistoryDetailsModel import com.tangem.features.tangempay.model.TangemPayTxHistoryDetailsModel
import com.tangem.features.tangempay.ui.TangemPayTxHistoryDetailsContent
import com.tangem.features.tangempay.ui.TangemPayTxHistoryDetailsContentV2 import com.tangem.features.tangempay.ui.TangemPayTxHistoryDetailsContentV2
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
@ -28,15 +25,8 @@ internal class TangemPayTxHistoryDetailsComponent @AssistedInject constructor(
@Composable @Composable
override fun BottomSheet() { override fun BottomSheet() {
val states by model.uiState.collectAsStateWithLifecycle() val state by model.uiState.collectAsStateWithLifecycle()
when (val uiState = states.toUiState(isRedesignEnabled = LocalVisaRedesignEnabled.current)) { TangemPayTxHistoryDetailsContentV2(state = state)
is TangemPayTxHistoryDetailsUiState.Legacy -> {
TangemPayTxHistoryDetailsContent(state = uiState.state)
}
is TangemPayTxHistoryDetailsUiState.Redesign -> {
TangemPayTxHistoryDetailsContentV2(state = uiState.state)
}
}
} }
@AssistedFactory @AssistedFactory

View file

@ -12,7 +12,6 @@ import com.tangem.core.ui.R as CoreUiR
@Immutable @Immutable
internal data class TangemPayCardPageUM( internal data class TangemPayCardPageUM(
val settings: ImmutableList<TangemPayCardPageSetting>,
val settingsV2: ImmutableList<TangemPayCardPageSettingV2>, val settingsV2: ImmutableList<TangemPayCardPageSettingV2>,
val onBackClick: () -> Unit, val onBackClick: () -> Unit,
val dailyLimitState: TangemPayDailyLimitBlockState, val dailyLimitState: TangemPayDailyLimitBlockState,
@ -26,17 +25,11 @@ internal data class TangemPayCardPageUM(
onClick = {}, onClick = {},
onClickClose = {}, onClickClose = {},
), ),
settings: ImmutableList<TangemPayCardPageSetting> = persistentListOf(
TangemPayCardPageSetting(TextReference.Str("Pin Code")) {},
TangemPayCardPageSetting(TextReference.Str("Freeze Card")) {},
TangemPayCardPageSetting(TextReference.Str("Reissue Card")) {},
),
cardState: TangemPayCardState = TangemPayCardState.Active, cardState: TangemPayCardState = TangemPayCardState.Active,
dailyLimitState: TangemPayDailyLimitBlockState = TangemPayDailyLimitBlockState.Content.stub(), dailyLimitState: TangemPayDailyLimitBlockState = TangemPayDailyLimitBlockState.Content.stub(),
settingsV2: ImmutableList<TangemPayCardPageSettingV2> = TangemPayCardPageSettingV2.stubList(), settingsV2: ImmutableList<TangemPayCardPageSettingV2> = TangemPayCardPageSettingV2.stubList(),
) = TangemPayCardPageUM( ) = TangemPayCardPageUM(
addToWalletBlockState = addToWalletBlockState, addToWalletBlockState = addToWalletBlockState,
settings = settings,
settingsV2 = settingsV2, settingsV2 = settingsV2,
onBackClick = {}, onBackClick = {},
cardState = cardState, cardState = cardState,
@ -46,13 +39,6 @@ internal data class TangemPayCardPageUM(
} }
} }
@Immutable
internal data class TangemPayCardPageSetting(
val title: TextReference,
val testTag: String? = null,
val onSettingClick: () -> Unit,
)
@Immutable @Immutable
internal data class TangemPayCardPageSettingV2( internal data class TangemPayCardPageSettingV2(
val id: Id, val id: Id,

View file

@ -15,18 +15,17 @@ internal class TangemPayDetailsNotificationFactory(
private val intents: TangemPayDetailIntents, private val intents: TangemPayDetailIntents,
private val isRemoveAccountEnabled: Boolean, private val isRemoveAccountEnabled: Boolean,
private val isTiersPlusPlanEnabled: Boolean, private val isTiersPlusPlanEnabled: Boolean,
private val isRedesignEnabled: Boolean,
) { ) {
fun createErrorConfig(error: PaymentAccountStatusValue.Error?): NotificationConfig? = when (error) { fun createErrorConfig(error: PaymentAccountStatusValue.Error?): NotificationConfig? = when (error) {
null -> null null -> null
PaymentAccountStatusValue.Error.NotSynced -> createRenewSessionNotificationConfig(isRedesignEnabled) PaymentAccountStatusValue.Error.NotSynced -> createRenewSessionNotificationConfig()
else -> createAccountUnavailableConfig(isRedesignEnabled) else -> createAccountUnavailableConfig()
} }
fun createAccountDeactivatedConfig(isRedesignEnabled: Boolean) = NotificationConfig( fun createAccountDeactivatedConfig() = NotificationConfig(
title = resourceReference(R.string.tangempay_account_deactivated_message_title), title = resourceReference(R.string.tangempay_account_deactivated_message_title),
subtitle = resourceReference(R.string.tangempay_account_deactivated_message_subtitle), subtitle = resourceReference(R.string.tangempay_account_deactivated_message_subtitle),
iconResId = if (isRedesignEnabled) R.drawable.ic_alert_circle_24 else R.drawable.img_attention_20, iconResId = R.drawable.ic_alert_circle_24,
buttonsState = if (isRemoveAccountEnabled) { buttonsState = if (isRemoveAccountEnabled) {
NotificationConfig.ButtonsState.SecondaryButtonConfig( NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(R.string.tangempay_remove_account), text = resourceReference(R.string.tangempay_remove_account),
@ -65,10 +64,10 @@ internal class TangemPayDetailsNotificationFactory(
) )
} }
private fun createRenewSessionNotificationConfig(isRedesignEnabled: Boolean) = NotificationConfig( private fun createRenewSessionNotificationConfig() = NotificationConfig(
title = resourceReference(R.string.tangempay_sync_needed_title), title = resourceReference(R.string.tangempay_sync_needed_title),
subtitle = resourceReference(R.string.tangempay_sync_needed_body), subtitle = resourceReference(R.string.tangempay_sync_needed_body),
iconResId = if (isRedesignEnabled) 0 else R.drawable.img_attention_20, iconResId = 0,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(R.string.tangempay_sync_needed_button), text = resourceReference(R.string.tangempay_sync_needed_button),
onClick = intents::onRenewSession, onClick = intents::onRenewSession,
@ -76,10 +75,10 @@ internal class TangemPayDetailsNotificationFactory(
), ),
) )
private fun createAccountUnavailableConfig(isRedesignEnabled: Boolean) = NotificationConfig( private fun createAccountUnavailableConfig() = NotificationConfig(
title = resourceReference(R.string.tangempay_temporarily_unavailable), title = resourceReference(R.string.tangempay_temporarily_unavailable),
subtitle = resourceReference(R.string.tangempay_service_unreachable_try_later), subtitle = resourceReference(R.string.tangempay_service_unreachable_try_later),
iconResId = if (isRedesignEnabled) R.drawable.ic_alert_circle_24 else R.drawable.img_attention_20, iconResId = R.drawable.ic_alert_circle_24,
) )
private fun createTariffSystemDownGradePendingConfig(tariffPlan: TangemPayTariffPlanState): NotificationConfig? { private fun createTariffSystemDownGradePendingConfig(tariffPlan: TangemPayTariffPlanState): NotificationConfig? {

View file

@ -33,7 +33,6 @@ internal class TangemPayDetailsStateFactory(
private val onBack: () -> Unit, private val onBack: () -> Unit,
private val onOpenMenu: () -> Unit, private val onOpenMenu: () -> Unit,
private val intents: TangemPayDetailIntents, private val intents: TangemPayDetailIntents,
private val isRedesignEnabled: Boolean,
private val isRemoveAccountEnabled: Boolean, private val isRemoveAccountEnabled: Boolean,
private val isTiersPlusPlanEnabled: Boolean, private val isTiersPlusPlanEnabled: Boolean,
) { ) {
@ -41,7 +40,6 @@ internal class TangemPayDetailsStateFactory(
intents = intents, intents = intents,
isRemoveAccountEnabled = isRemoveAccountEnabled, isRemoveAccountEnabled = isRemoveAccountEnabled,
isTiersPlusPlanEnabled = isTiersPlusPlanEnabled, isTiersPlusPlanEnabled = isTiersPlusPlanEnabled,
isRedesignEnabled = isRedesignEnabled,
) )
fun getLoadingState(): TangemPayDetailsUM { fun getLoadingState(): TangemPayDetailsUM {
@ -130,7 +128,7 @@ internal class TangemPayDetailsStateFactory(
} }
fun getDeactivatedState(hasWithdrawableBalance: Boolean): TangemPayDetailsUM { fun getDeactivatedState(hasWithdrawableBalance: Boolean): TangemPayDetailsUM {
val accountDeactivatedNotification = notificationFactory.createAccountDeactivatedConfig(isRedesignEnabled) val accountDeactivatedNotification = notificationFactory.createAccountDeactivatedConfig()
return TangemPayDetailsUM( return TangemPayDetailsUM(
topBarConfig = TangemPayDetailsTopBarConfig( topBarConfig = TangemPayDetailsTopBarConfig(
onBackClick = onBack, onBackClick = onBack,
@ -327,11 +325,7 @@ internal class TangemPayDetailsStateFactory(
action = TangemPayAction.AddFunds, action = TangemPayAction.AddFunds,
config = ActionButtonConfig( config = ActionButtonConfig(
text = resourceReference(id = R.string.tangempay_card_details_add_funds), text = resourceReference(id = R.string.tangempay_card_details_add_funds),
iconResId = if (isRedesignEnabled) { iconResId = R.drawable.ic_arrow_down_24,
R.drawable.ic_arrow_down_24
} else {
R.drawable.ic_plus_24
},
onClick = intents::onClickAddFunds, onClick = intents::onClickAddFunds,
isEnabled = isAddFundsEnabled, isEnabled = isAddFundsEnabled,
), ),

View file

@ -1,36 +1,9 @@
package com.tangem.features.tangempay.entity package com.tangem.features.tangempay.entity
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.ColorReference
import com.tangem.core.ui.extensions.ImageReference import com.tangem.core.ui.extensions.ImageReference
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
internal data class TangemPayTxHistoryDetailsUM(
val isBalanceHidden: Boolean,
val title: TextReference,
val iconState: ImageReference,
val transactionTitle: TextReference,
val transactionSubtitle: TextReference,
val transactionAmount: String,
val transactionAmountColor: ColorReference,
val localTransactionText: String?,
val labelState: LabelUM?,
val notification: NotificationState?,
val buttons: ImmutableList<ButtonState>,
val dismiss: () -> Unit,
) {
data class NotificationState(
val config: NotificationConfig,
val titleColor: ColorReference,
val iconTint: ColorReference,
val containerColor: ColorReference?,
)
}
internal data class ButtonState( internal data class ButtonState(
val text: TextReference, val text: TextReference,
@ -78,23 +51,4 @@ internal sealed interface TransactionDetailUM {
data class Error(val onRefreshClick: () -> Unit) : TransactionDetailUM data class Error(val onRefreshClick: () -> Unit) : TransactionDetailUM
} }
internal enum class TransactionLoadState { Loading, Loaded, Error } internal enum class TransactionLoadState { Loading, Loaded, Error }
@Immutable
internal sealed interface TangemPayTxHistoryDetailsUiState {
data class Legacy(val state: TangemPayTxHistoryDetailsUM) : TangemPayTxHistoryDetailsUiState
data class Redesign(val state: TangemPayTxHistoryDetailsUMV2) : TangemPayTxHistoryDetailsUiState
}
internal data class TangemPayTxHistoryDetailsUiStates(
val legacy: TangemPayTxHistoryDetailsUM,
val redesign: TangemPayTxHistoryDetailsUMV2,
) {
fun toUiState(isRedesignEnabled: Boolean): TangemPayTxHistoryDetailsUiState {
return if (isRedesignEnabled) {
TangemPayTxHistoryDetailsUiState.Redesign(redesign)
} else {
TangemPayTxHistoryDetailsUiState.Legacy(legacy)
}
}
}

View file

@ -22,17 +22,10 @@ internal class TangemPayCardLimitSetupComponent(
override fun Content(modifier: Modifier) { override fun Content(modifier: Modifier) {
val state by model.uiState.collectAsStateWithLifecycle() val state by model.uiState.collectAsStateWithLifecycle()
BackHandler(onBack = router::pop) BackHandler(onBack = router::pop)
if (model.inRedesignEnabled()) { TangemPayCardLimitSetupScreenV2(
TangemPayCardLimitSetupScreenV2( state = state,
state = state, modifier = modifier,
modifier = modifier, )
)
} else {
TangemPayCardLimitSetupScreen(
state = state,
modifier = modifier,
)
}
} }
data class Params(val card: TangemPayCard, val userWalletId: UserWalletId) data class Params(val card: TangemPayCard, val userWalletId: UserWalletId)

View file

@ -23,7 +23,6 @@ import com.tangem.domain.models.pay.TangemPayCardLimitPeriod
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
import com.tangem.domain.pay.usecase.SetTangemPayCardLimitUseCase import com.tangem.domain.pay.usecase.SetTangemPayCardLimitUseCase
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -46,7 +45,6 @@ internal class TangemPayCardLimitSetupModel @Inject constructor(
private val setTangemPayCardLimitUseCase: SetTangemPayCardLimitUseCase, private val setTangemPayCardLimitUseCase: SetTangemPayCardLimitUseCase,
private val uiMessageSender: UiMessageSender, private val uiMessageSender: UiMessageSender,
private val analytics: AnalyticsEventHandler, private val analytics: AnalyticsEventHandler,
private val featureToggles: TangemPayFeatureToggles,
) : Model() { ) : Model() {
private val params: TangemPayCardLimitSetupComponent.Params = paramsContainer.require() private val params: TangemPayCardLimitSetupComponent.Params = paramsContainer.require()
@ -78,8 +76,6 @@ internal class TangemPayCardLimitSetupModel @Inject constructor(
observeCardState() observeCardState()
} }
fun inRedesignEnabled(): Boolean = featureToggles.isRedesignEnabled
private fun observeCardState() { private fun observeCardState() {
paymentAccountStatusSupplier.invoke(userWalletId) paymentAccountStatusSupplier.invoke(userWalletId)
.map { it.value } .map { it.value }

View file

@ -1,185 +0,0 @@
package com.tangem.features.tangempay.limit.setup
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SpacerH12
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.appbar.TangemTopAppBar
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.components.fields.AmountTextField
import com.tangem.core.ui.components.fields.TangemAmountTextFieldColors
import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.utils.rememberDecimalFormat
import com.tangem.features.tangempay.details.impl.R
import kotlinx.collections.immutable.ImmutableList
@Composable
internal fun TangemPayCardLimitSetupScreen(state: TangemPayCardLimitSetupUM, modifier: Modifier = Modifier) {
Scaffold(
modifier = modifier,
topBar = {
TangemTopAppBar(
modifier = Modifier.statusBarsPadding(),
title = stringResourceSafe(R.string.tangempay_card_page_daily_limit_title),
startButton = TopAppBarButtonUM.Close(onCloseClick = state.onBackClick),
)
},
containerColor = TangemTheme.colors.background.secondary,
) { scaffoldPaddings ->
Content(
state = state,
modifier = Modifier.padding(scaffoldPaddings),
)
}
}
@Composable
private fun Content(state: TangemPayCardLimitSetupUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxSize()
.imePadding(),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
AmountBlock(
modifier = Modifier.padding(horizontal = 16.dp),
state = state,
)
Spacer(modifier = Modifier.weight(1f))
PrimaryButton(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
text = stringResourceSafe(R.string.tangempay_daily_limit_set_button),
enabled = state.isSubmitButtonEnabled,
showProgress = state.isSubmitButtonLoading,
onClick = state.onSubmitClick,
)
PresetsRow(presets = state.presets)
}
}
@Composable
private fun AmountBlock(state: TangemPayCardLimitSetupUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxWidth()
.clip(RoundedCornerShape(14.dp))
.background(TangemTheme.colors.background.action)
.padding(vertical = 48.dp, horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResourceSafe(R.string.common_amount),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
SpacerH12()
if (state.isInitialDataLoading) {
TextShimmer(
style = TangemTheme.typography.head,
text = "$5000",
)
} else {
val colors = TangemAmountTextFieldColors
AmountTextField(
value = state.amountFieldModel.value,
decimals = state.amountFieldModel.decimals,
onValueChange = state.amountFieldModel.onValueChange,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
colors = colors,
visualTransformation = AmountVisualTransformation(
decimals = state.amountFieldModel.decimals,
symbol = state.currencyCode,
currencyCode = state.currencyCode,
decimalFormat = rememberDecimalFormat(),
symbolColor = if (state.amountFieldModel.value.isBlank()) {
colors.disabledTextColor
} else {
colors.textColor
},
),
textStyle = TangemTheme.typography.head.copy(
textAlign = TextAlign.Center,
),
isAutoResize = true,
)
}
Spacer(modifier = Modifier.padding(top = 8.dp))
Text(
text = state.subtitle.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
)
}
}
@Composable
private fun PresetsRow(presets: ImmutableList<TangemPayCardLimitSetupUM.LimitPresetUM>) {
if (presets.isEmpty()) return
Row(
modifier = Modifier
.fillMaxWidth()
.background(color = TangemTheme.colors.button.secondary)
.padding(horizontal = 8.dp, vertical = 12.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
presets.forEach { preset ->
PresetChip(
preset = preset,
modifier = Modifier.weight(1f),
)
}
}
}
@Composable
private fun PresetChip(preset: TangemPayCardLimitSetupUM.LimitPresetUM, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.clip(RoundedCornerShape(16.dp))
.background(TangemTheme.colors.background.primary)
.clickable(onClick = preset.onClick)
.padding(horizontal = 12.dp, vertical = 4.dp)
.wrapContentHeight(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = preset.label,
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
maxLines = 1,
)
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun preview() = TangemThemePreview {
TangemPayCardLimitSetupScreen(
state = TangemPayCardLimitSetupUM.stub(),
)
}

View file

@ -8,24 +8,16 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute import com.tangem.features.tangempay.navigation.TangemPayCardDetailsInnerRoute
internal class TangemPayCardLimitSetupSuccessComponent( internal class TangemPayCardLimitSetupSuccessComponent(
private val isRedesignEnabled: Boolean,
appComponentContext: AppComponentContext, appComponentContext: AppComponentContext,
) : AppComponentContext by appComponentContext, ComposableContentComponent { ) : AppComponentContext by appComponentContext, ComposableContentComponent {
@Composable @Composable
override fun Content(modifier: Modifier) { override fun Content(modifier: Modifier) {
BackHandler(onBack = ::backToDetails) BackHandler(onBack = ::backToDetails)
if (isRedesignEnabled) { TangemPayCardLimitSetupSuccessScreenV2(
TangemPayCardLimitSetupSuccessScreenV2( modifier = modifier,
modifier = modifier, onDoneClick = ::backToDetails,
onDoneClick = ::backToDetails, )
)
} else {
TangemPayCardLimitSetupSuccessScreen(
modifier = modifier,
onDoneClick = ::backToDetails,
)
}
} }
private fun backToDetails() { private fun backToDetails() {

View file

@ -1,102 +0,0 @@
package com.tangem.features.tangempay.limit.setup
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SpacerH12
import com.tangem.core.ui.components.SpacerH32
import com.tangem.core.ui.components.appbar.TangemTopAppBar
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.tangempay.details.impl.R
@Composable
internal fun TangemPayCardLimitSetupSuccessScreen(onDoneClick: () -> Unit, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxSize()
.navigationBarsPadding(),
) {
TangemTopAppBar(
modifier = Modifier.statusBarsPadding(),
startButton = TopAppBarButtonUM.Close(onCloseClick = onDoneClick),
)
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SuccessContent(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
)
PrimaryButton(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 16.dp),
text = stringResourceSafe(R.string.common_done),
onClick = onDoneClick,
)
}
}
}
@Composable
private fun SuccessContent(modifier: Modifier = Modifier) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_success_blue_76),
tint = Color.Unspecified,
contentDescription = null,
modifier = Modifier.size(76.dp),
)
SpacerH32()
Text(
modifier = Modifier.padding(horizontal = 32.dp),
text = stringResourceSafe(R.string.tangempay_card_page_daily_limit_success_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
SpacerH12()
Text(
modifier = Modifier.padding(horizontal = 32.dp),
text = stringResourceSafe(R.string.tangempay_card_page_daily_limit_success_description),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
)
}
}
@Preview(showBackground = true)
@Composable
private fun Preview() {
TangemThemePreview {
TangemPayCardLimitSetupSuccessScreen(onDoneClick = {})
}
}

View file

@ -9,7 +9,6 @@ import com.tangem.domain.models.ReceiveAddressModel
import com.tangem.domain.models.ReceiveAddressModel.DisplayType import com.tangem.domain.models.ReceiveAddressModel.DisplayType
import com.tangem.domain.pay.model.TangemPayTopUpData import com.tangem.domain.pay.model.TangemPayTopUpData
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayAddFundsComponent import com.tangem.features.tangempay.components.TangemPayAddFundsComponent
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
import com.tangem.features.tangempay.model.transformers.TangemPayAddFundsUMConverter import com.tangem.features.tangempay.model.transformers.TangemPayAddFundsUMConverter
@ -22,7 +21,6 @@ import javax.inject.Inject
internal class TangemPayAddFundsModel @Inject constructor( internal class TangemPayAddFundsModel @Inject constructor(
paramsContainer: ParamsContainer, paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider, override val dispatchers: CoroutineDispatcherProvider,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
virtualAccountToggles: VirtualAccountFeatureToggles, virtualAccountToggles: VirtualAccountFeatureToggles,
analytics: AnalyticsEventHandler, analytics: AnalyticsEventHandler,
) : Model() { ) : Model() {
@ -55,13 +53,10 @@ internal class TangemPayAddFundsModel @Inject constructor(
) )
return TangemPayAddFundsUMConverter( return TangemPayAddFundsUMConverter(
listener = params.listener, listener = params.listener,
isRedesignEnabled = tangemPayFeatureToggles.isRedesignEnabled,
shouldShowBankTransfer = isBankTransferShown, shouldShowBankTransfer = isBankTransferShown,
).convert(data) ).convert(data)
} }
fun isRedesignEnabled(): Boolean = tangemPayFeatureToggles.isRedesignEnabled
fun onDismiss() { fun onDismiss() {
params.listener.onDismissAddFunds() params.listener.onDismissAddFunds()
} }

View file

@ -6,7 +6,6 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayAddToWalletComponent import com.tangem.features.tangempay.components.TangemPayAddToWalletComponent
import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayAddToWalletStepItemUM import com.tangem.features.tangempay.entity.TangemPayAddToWalletStepItemUM
@ -27,7 +26,6 @@ internal class TangemPayAddToWalletModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider, override val dispatchers: CoroutineDispatcherProvider,
private val router: Router, private val router: Router,
private val googleWalletUtil: GoogleWalletUtil, private val googleWalletUtil: GoogleWalletUtil,
private val featureToggles: TangemPayFeatureToggles,
cardDetailsControllerFactory: TangemPayCardDetailsController.Factory, cardDetailsControllerFactory: TangemPayCardDetailsController.Factory,
) : Model() { ) : Model() {
@ -49,8 +47,6 @@ internal class TangemPayAddToWalletModel @Inject constructor(
val uiState: StateFlow<TangemPayAddToWalletUM> val uiState: StateFlow<TangemPayAddToWalletUM>
field = MutableStateFlow(getInitialState()) field = MutableStateFlow(getInitialState())
fun isRedesignEnabled(): Boolean = featureToggles.isRedesignEnabled
@Suppress("MagicNumber") @Suppress("MagicNumber")
private fun getInitialState(): TangemPayAddToWalletUM { private fun getInitialState(): TangemPayAddToWalletUM {
return TangemPayAddToWalletUM( return TangemPayAddToWalletUM(

View file

@ -40,7 +40,6 @@ import com.tangem.domain.pay.model.TangemPayTopUpData
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.pay.usecase.ChangeCardFrozenStateUseCase import com.tangem.domain.pay.usecase.ChangeCardFrozenStateUseCase
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.closure.CloseCardListener import com.tangem.features.tangempay.closure.CloseCardListener
import com.tangem.features.tangempay.components.AddFundsListener import com.tangem.features.tangempay.components.AddFundsListener
import com.tangem.features.tangempay.components.ReissueCardListener import com.tangem.features.tangempay.components.ReissueCardListener
@ -81,7 +80,6 @@ internal class TangemPayCardPageModel @Inject constructor(
private val uiMessageSender: UiMessageSender, private val uiMessageSender: UiMessageSender,
private val changeCardFrozenStateUseCase: ChangeCardFrozenStateUseCase, private val changeCardFrozenStateUseCase: ChangeCardFrozenStateUseCase,
private val cardDetailsEventListener: CardDetailsEventListener, private val cardDetailsEventListener: CardDetailsEventListener,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val cardDetailsControllerFactory: TangemPayCardDetailsController.Factory, private val cardDetailsControllerFactory: TangemPayCardDetailsController.Factory,
) : Model(), ViewPinListener, ReissueCardListener, AddFundsListener, CloseCardListener { ) : Model(), ViewPinListener, ReissueCardListener, AddFundsListener, CloseCardListener {
@ -116,7 +114,6 @@ internal class TangemPayCardPageModel @Inject constructor(
TangemPayCardPageUM( TangemPayCardPageUM(
onBackClick = router::pop, onBackClick = router::pop,
dailyLimitState = TangemPayDailyLimitBlockState.Loading, dailyLimitState = TangemPayDailyLimitBlockState.Loading,
settings = persistentListOf(),
settingsV2 = persistentListOf(), settingsV2 = persistentListOf(),
menuItems = buildMenuItems( menuItems = buildMenuItems(
isLastCard = params.initialStatus.ifLoadedOrNull { it.cards.isLastCard() } ?: false, isLastCard = params.initialStatus.ifLoadedOrNull { it.cards.isLastCard() } ?: false,
@ -170,8 +167,6 @@ internal class TangemPayCardPageModel @Inject constructor(
} }
} }
fun isRedesignEnabled(): Boolean = tangemPayFeatureToggles.isRedesignEnabled
/** Reports the card the user swiped to so per-card UI and reveal target it. */ /** Reports the card the user swiped to so per-card UI and reveal target it. */
fun onCardPageSelected(index: Int) { fun onCardPageSelected(index: Int) {
cardControllersState.value.getOrNull(index)?.let { selectedCardId.value = it.cardId } cardControllersState.value.getOrNull(index)?.let { selectedCardId.value = it.cardId }
@ -223,7 +218,6 @@ internal class TangemPayCardPageModel @Inject constructor(
uiState.update { uiState -> uiState.update { uiState ->
uiState.copy( uiState.copy(
dailyLimitState = buildDailyLimitState(state), dailyLimitState = buildDailyLimitState(state),
settings = buildSettings(card),
settingsV2 = buildSettingsV2(card), settingsV2 = buildSettingsV2(card),
menuItems = buildMenuItems(isLastCard = status.cards.isLastCard()), menuItems = buildMenuItems(isLastCard = status.cards.isLastCard()),
cardState = card.state, cardState = card.state,
@ -239,34 +233,7 @@ internal class TangemPayCardPageModel @Inject constructor(
return (status as? PaymentAccountStatusValue.Loaded)?.findCardWithId(selectedCardId.value) return (status as? PaymentAccountStatusValue.Loaded)?.findCardWithId(selectedCardId.value)
} }
private fun buildSettings(card: TangemPayCard): ImmutableList<TangemPayCardPageSetting> {
if (isRedesignEnabled()) return persistentListOf()
return persistentListOf(
TangemPayCardPageSetting(
title = TextReference.Res(R.string.tangempay_card_details_change_pin),
onSettingClick = { onClickChangePIN(card.hasPinCode) },
testTag = TangemPayTestTags.CHANGE_PIN_ROW,
),
TangemPayCardPageSetting(
title = TextReference.Res(
if (card.isFrozen) {
R.string.tangempay_card_details_unfreeze_card
} else {
R.string.tangempay_card_details_freeze_card
},
),
onSettingClick = { onClickFreezeOrUnfreezeCard(card.isFrozen) },
testTag = TangemPayTestTags.FREEZE_CARD_ROW,
),
TangemPayCardPageSetting(
title = TextReference.Res(R.string.tangempay_card_details_reissue_card),
onSettingClick = ::onClickReissueCard,
),
)
}
private suspend fun subscribeOnDetailsState() { private suspend fun subscribeOnDetailsState() {
if (!isRedesignEnabled()) return
combine(cardDetailsEventListener.event, selectedCardId) { event, selectedId -> combine(cardDetailsEventListener.event, selectedCardId) { event, selectedId ->
event is CardDetailsEvent.Show && event.cardId == selectedId event is CardDetailsEvent.Show && event.cardId == selectedId
}.collect { isDetailsShown -> }.collect { isDetailsShown ->
@ -287,7 +254,6 @@ internal class TangemPayCardPageModel @Inject constructor(
} }
private fun buildSettingsV2(card: TangemPayCard): ImmutableList<TangemPayCardPageSettingV2> { private fun buildSettingsV2(card: TangemPayCard): ImmutableList<TangemPayCardPageSettingV2> {
if (!isRedesignEnabled()) return persistentListOf()
return persistentListOf( return persistentListOf(
TangemPayCardPageSettingV2( TangemPayCardPageSettingV2(
id = TangemPayCardPageSettingV2.Id.Details, id = TangemPayCardPageSettingV2.Id.Details,
@ -395,15 +361,9 @@ internal class TangemPayCardPageModel @Inject constructor(
if (frozenStateJobHolder.isActive) return if (frozenStateJobHolder.isActive) return
val message = if (isFrozen) { val message = if (isFrozen) {
TangemPayMessagesFactory.createUnfreezeCardMessage( TangemPayMessagesFactory.createUnfreezeCardMessage(onUnfreezeClicked = ::unfreezeCard)
onUnfreezeClicked = ::unfreezeCard,
isRedesignEnabled = tangemPayFeatureToggles.isRedesignEnabled,
)
} else { } else {
TangemPayMessagesFactory.createFreezeCardMessage( TangemPayMessagesFactory.createFreezeCardMessage(onFreezeClicked = ::freezeCard)
onFreezeClicked = ::freezeCard,
isRedesignEnabled = tangemPayFeatureToggles.isRedesignEnabled,
)
} }
uiMessageSender.send(message) uiMessageSender.send(message)
} }

View file

@ -13,7 +13,6 @@ import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
import com.tangem.domain.pay.model.SetPinResult import com.tangem.domain.pay.model.SetPinResult
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayChangePinComponent import com.tangem.features.tangempay.components.TangemPayChangePinComponent
import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayChangePinUM import com.tangem.features.tangempay.entity.TangemPayChangePinUM
@ -38,7 +37,6 @@ internal class TangemPayChangePinModel @Inject constructor(
private val router: Router, private val router: Router,
private val cardDetailsRepository: TangemPayCardDetailsRepository, private val cardDetailsRepository: TangemPayCardDetailsRepository,
private val analytics: AnalyticsEventHandler, private val analytics: AnalyticsEventHandler,
private val featureToggles: TangemPayFeatureToggles,
private val fetcher: PaymentAccountStatusFetcher, private val fetcher: PaymentAccountStatusFetcher,
) : Model() { ) : Model() {
@ -50,13 +48,11 @@ internal class TangemPayChangePinModel @Inject constructor(
analytics.send(TangemPayAnalyticsEvents.ChangePinScreenShown()) analytics.send(TangemPayAnalyticsEvents.ChangePinScreenShown())
} }
fun isRedesignEnabled(): Boolean = featureToggles.isRedesignEnabled
private fun onPinCodeChange(pin: String) { private fun onPinCodeChange(pin: String) {
uiState.update(transformer = PinCodeChangeTransformer(newPin = pin)) uiState.update(transformer = PinCodeChangeTransformer(newPin = pin))
val state = uiState.value val state = uiState.value
// In the redesign there is no submit button: a valid full PIN is submitted automatically. // There is no submit button: a valid full PIN is submitted automatically.
if (featureToggles.isRedesignEnabled && state.submitButtonEnabled && !state.submitButtonLoading) { if (state.submitButtonEnabled && !state.submitButtonLoading) {
onClickSubmit() onClickSubmit()
} }
} }

View file

@ -115,7 +115,6 @@ internal class TangemPayDetailsModel @Inject constructor(
onBack = router::pop, onBack = router::pop,
onOpenMenu = ::onOpenMenu, onOpenMenu = ::onOpenMenu,
intents = this, intents = this,
isRedesignEnabled = isRedesignEnabled(),
isRemoveAccountEnabled = tangemPayFeatureToggles.isRemoveAccountEnabled, isRemoveAccountEnabled = tangemPayFeatureToggles.isRemoveAccountEnabled,
isTiersPlusPlanEnabled = tangemPayFeatureToggles.isTiersPlusPlanEnabled, isTiersPlusPlanEnabled = tangemPayFeatureToggles.isTiersPlusPlanEnabled,
) )
@ -198,8 +197,6 @@ internal class TangemPayDetailsModel @Inject constructor(
} }
} }
fun isRedesignEnabled(): Boolean = tangemPayFeatureToggles.isRedesignEnabled
override fun onClickAddFunds() { override fun onClickAddFunds() {
analytics.send(TangemPayAnalyticsEvents.AddFundsClicked()) analytics.send(TangemPayAnalyticsEvents.AddFundsClicked())
val balance = currentStatus.value.balanceOrNull() val balance = currentStatus.value.balanceOrNull()

View file

@ -18,7 +18,6 @@ import com.tangem.domain.models.account.requireCardWithId
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
import com.tangem.domain.pay.usecase.UpdateTangemPayCardNameUseCase import com.tangem.domain.pay.usecase.UpdateTangemPayCardNameUseCase
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayEditDisplayNameComponent import com.tangem.features.tangempay.components.TangemPayEditDisplayNameComponent
import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM
@ -39,7 +38,6 @@ internal class TangemPayEditDisplayNameModel @Inject constructor(
private val updateCardNameUseCase: UpdateTangemPayCardNameUseCase, private val updateCardNameUseCase: UpdateTangemPayCardNameUseCase,
private val uiMessageSender: UiMessageSender, private val uiMessageSender: UiMessageSender,
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier, private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier,
private val featureToggles: TangemPayFeatureToggles,
cardDetailsControllerFactory: TangemPayCardDetailsController.Factory, cardDetailsControllerFactory: TangemPayCardDetailsController.Factory,
) : Model() { ) : Model() {
@ -80,8 +78,6 @@ internal class TangemPayEditDisplayNameModel @Inject constructor(
subscribeToCardNameChanges(card.id, params.userWalletId) subscribeToCardNameChanges(card.id, params.userWalletId)
} }
fun isRedesignEnabled() = featureToggles.isRedesignEnabled
private fun subscribeToCardNameChanges(cardId: String, userWalletId: UserWalletId) { private fun subscribeToCardNameChanges(cardId: String, userWalletId: UserWalletId) {
paymentAccountStatusSupplier.invoke(userWalletId) paymentAccountStatusSupplier.invoke(userWalletId)
.onEach { state -> .onEach { state ->

View file

@ -15,7 +15,6 @@ import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.pay.repository.TangemPayReissueCardRepository import com.tangem.domain.pay.repository.TangemPayReissueCardRepository
import com.tangem.domain.pay.usecase.ReissueTangemPayCardUseCase import com.tangem.domain.pay.usecase.ReissueTangemPayCardUseCase
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayReissueCardComponent import com.tangem.features.tangempay.components.TangemPayReissueCardComponent
import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayReissueCardError import com.tangem.features.tangempay.entity.TangemPayReissueCardError
@ -42,7 +41,6 @@ internal class TangemPayReissueCardModel @Inject constructor(
private val reissueTangemPayCardUseCase: ReissueTangemPayCardUseCase, private val reissueTangemPayCardUseCase: ReissueTangemPayCardUseCase,
private val uiMessageSender: UiMessageSender, private val uiMessageSender: UiMessageSender,
private val analytics: AnalyticsEventHandler, private val analytics: AnalyticsEventHandler,
private val featureToggles: TangemPayFeatureToggles,
) : Model() { ) : Model() {
private val params = paramsContainer.require<TangemPayReissueCardComponent.Params>() private val params = paramsContainer.require<TangemPayReissueCardComponent.Params>()
@ -69,8 +67,6 @@ internal class TangemPayReissueCardModel @Inject constructor(
loadData() loadData()
} }
fun isRedesignEnabled(): Boolean = featureToggles.isRedesignEnabled
fun onDismiss() { fun onDismiss() {
reissueJobHolder.cancel() reissueJobHolder.cancel()
params.listener.onDismissReissueCard() params.listener.onDismissReissueCard()

View file

@ -16,9 +16,8 @@ import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
import com.tangem.domain.visa.model.TangemPayTxHistoryItem import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.features.tangempay.components.TangemPayTransactionBottomSheetComponent import com.tangem.features.tangempay.components.TangemPayTransactionBottomSheetComponent
import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUiStates import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUMV2
import com.tangem.features.tangempay.entity.TransactionLoadState import com.tangem.features.tangempay.entity.TransactionLoadState
import com.tangem.features.tangempay.model.transformers.TangemPayTxHistoryDetailsConverter
import com.tangem.features.tangempay.model.transformers.TangemPayTxHistoryDetailsConverterV2 import com.tangem.features.tangempay.model.transformers.TangemPayTxHistoryDetailsConverterV2
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -46,16 +45,16 @@ internal class TangemPayTxHistoryDetailsModel @Inject constructor(
private val transactionLoadState = MutableStateFlow(TransactionLoadState.Loading) private val transactionLoadState = MutableStateFlow(TransactionLoadState.Loading)
private var loadTransactionJob: Job? = null private var loadTransactionJob: Job? = null
val uiState: StateFlow<TangemPayTxHistoryDetailsUiStates> = combine( val uiState: StateFlow<TangemPayTxHistoryDetailsUMV2> = combine(
balanceHidingSettings.isBalanceHidden(), balanceHidingSettings.isBalanceHidden(),
transaction, transaction,
transactionLoadState, transactionLoadState,
) { isBalanceHidden, transaction, transactionLoadState -> ) { isBalanceHidden, transaction, transactionLoadState ->
buildUiStates(isBalanceHidden, transaction, transactionLoadState) buildUiState(isBalanceHidden, transaction, transactionLoadState)
}.stateIn( }.stateIn(
scope = modelScope, scope = modelScope,
started = SharingStarted.Eagerly, started = SharingStarted.Eagerly,
initialValue = buildUiStates( initialValue = buildUiState(
isBalanceHidden = params.isBalanceHidden, isBalanceHidden = params.isBalanceHidden,
transaction = params.transaction, transaction = params.transaction,
transactionLoadState = transactionLoadState.value, transactionLoadState = transactionLoadState.value,
@ -88,30 +87,20 @@ internal class TangemPayTxHistoryDetailsModel @Inject constructor(
} }
} }
private fun buildUiStates( private fun buildUiState(
isBalanceHidden: Boolean, isBalanceHidden: Boolean,
transaction: TangemPayTxHistoryItem, transaction: TangemPayTxHistoryItem,
transactionLoadState: TransactionLoadState, transactionLoadState: TransactionLoadState,
): TangemPayTxHistoryDetailsUiStates { ): TangemPayTxHistoryDetailsUMV2 {
val converterInput = TangemPayTxHistoryDetailsConverter.Input( return TangemPayTxHistoryDetailsConverterV2.convert(
item = transaction, value = TangemPayTxHistoryDetailsConverterV2.Input(
isBalanceHidden = isBalanceHidden, item = transaction,
onExplorerClick = ::openExplorer, isBalanceHidden = isBalanceHidden,
onDisputeClick = { dispute(customerId = params.customerId) }, transactionLoadState = transactionLoadState,
onDismiss = ::dismiss, onExplorerClick = ::openExplorer,
) onDisputeClick = { dispute(customerId = params.customerId) },
return TangemPayTxHistoryDetailsUiStates( onCardRefreshClick = ::loadTransaction,
legacy = TangemPayTxHistoryDetailsConverter.convert(converterInput), onDismiss = ::dismiss,
redesign = TangemPayTxHistoryDetailsConverterV2.convert(
value = TangemPayTxHistoryDetailsConverterV2.Input(
item = converterInput.item,
isBalanceHidden = converterInput.isBalanceHidden,
transactionLoadState = transactionLoadState,
onExplorerClick = converterInput.onExplorerClick,
onDisputeClick = converterInput.onDisputeClick,
onCardRefreshClick = ::loadTransaction,
onDismiss = converterInput.onDismiss,
),
), ),
) )
} }

View file

@ -6,7 +6,6 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.txHistory.DefaultTangemPayTxHistoryComponent import com.tangem.features.tangempay.components.txHistory.DefaultTangemPayTxHistoryComponent
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
import com.tangem.features.tangempay.utils.TangemPayTxHistoryListManager import com.tangem.features.tangempay.utils.TangemPayTxHistoryListManager
@ -26,7 +25,6 @@ internal class TangemPayTxHistoryModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider, override val dispatchers: CoroutineDispatcherProvider,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val txHistoryUpdateListener: TangemPayTxHistoryUpdateListener, private val txHistoryUpdateListener: TangemPayTxHistoryUpdateListener,
private val featureToggles: TangemPayFeatureToggles,
) : Model() { ) : Model() {
private val params: DefaultTangemPayTxHistoryComponent.Params = paramsContainer.require() private val params: DefaultTangemPayTxHistoryComponent.Params = paramsContainer.require()
@ -46,8 +44,6 @@ internal class TangemPayTxHistoryModel @Inject constructor(
subscribeToUpdateListener() subscribeToUpdateListener()
} }
fun isRedesignEnabled(): Boolean = featureToggles.isRedesignEnabled
private fun launchPagination() { private fun launchPagination() {
modelScope.launch { listManager.launchPagination(params.userWalletId) } modelScope.launch { listManager.launchPagination(params.userWalletId) }
} }

View file

@ -7,7 +7,6 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayViewPinComponent import com.tangem.features.tangempay.components.TangemPayViewPinComponent
import com.tangem.features.tangempay.entity.TangemPayViewPinUM import com.tangem.features.tangempay.entity.TangemPayViewPinUM
import com.tangem.features.tangempay.model.transformers.TangemPayViewPinErrorStateTransformer import com.tangem.features.tangempay.model.transformers.TangemPayViewPinErrorStateTransformer
@ -26,7 +25,6 @@ internal class TangemPayViewPinModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider, override val dispatchers: CoroutineDispatcherProvider,
private val cardDetailsRepository: TangemPayCardDetailsRepository, private val cardDetailsRepository: TangemPayCardDetailsRepository,
private val analytics: AnalyticsEventHandler, private val analytics: AnalyticsEventHandler,
private val featureToggles: TangemPayFeatureToggles,
) : Model() { ) : Model() {
private val params = paramsContainer.require<TangemPayViewPinComponent.Params>() private val params = paramsContainer.require<TangemPayViewPinComponent.Params>()
@ -39,8 +37,6 @@ internal class TangemPayViewPinModel @Inject constructor(
getCardPin() getCardPin()
} }
fun isRedesignEnabled(): Boolean = featureToggles.isRedesignEnabled
private fun getCardPin() { private fun getCardPin() {
modelScope.launch { modelScope.launch {
cardDetailsRepository.getPin(userWalletId = params.walletId, cardId = params.cardId) cardDetailsRepository.getPin(userWalletId = params.walletId, cardId = params.cardId)

View file

@ -18,7 +18,6 @@ import kotlinx.collections.immutable.toPersistentList
internal class TangemPayAddFundsUMConverter( internal class TangemPayAddFundsUMConverter(
val listener: AddFundsListener, val listener: AddFundsListener,
val isRedesignEnabled: Boolean,
val shouldShowBankTransfer: Boolean, val shouldShowBankTransfer: Boolean,
) : Converter<TangemPayTopUpData, TangemPayAddFundsUM> { ) : Converter<TangemPayTopUpData, TangemPayAddFundsUM> {
@ -27,41 +26,23 @@ internal class TangemPayAddFundsUMConverter(
return TangemPayAddFundsUM( return TangemPayAddFundsUM(
items = buildList { items = buildList {
TangemPayAddFundsItemUM( TangemPayAddFundsItemUM(
icon = if (isRedesignEnabled) { icon = TangemIconUM.Icon(
TangemIconUM.Icon( imageVector = Icons.ic_logo_tangem_20,
imageVector = Icons.ic_logo_tangem_20, tintReference = {
tintReference = { TangemTheme.colors3.icon.brand
TangemTheme.colors3.icon.brand },
}, ),
)
} else {
TangemIconUM.Icon(
iconRes = R.drawable.ic_exchange_vertical_24,
tintReference = {
TangemTheme.colors.icon.accent
},
)
},
title = resourceReference(R.string.tangempay_topup_swap_title), title = resourceReference(R.string.tangempay_topup_swap_title),
description = resourceReference(R.string.tangempay_topup_swap_body), description = resourceReference(R.string.tangempay_topup_swap_body),
onClick = { listener.onClickSwap(value) }, onClick = { listener.onClickSwap(value) },
).let(::add) ).let(::add)
TangemPayAddFundsItemUM( TangemPayAddFundsItemUM(
icon = if (isRedesignEnabled) { icon = TangemIconUM.Icon(
TangemIconUM.Icon( imageVector = Icons.ic_card_20,
imageVector = Icons.ic_card_20, tintReference = {
tintReference = { TangemTheme.colors3.icon.brand
TangemTheme.colors3.icon.brand },
}, ),
)
} else {
TangemIconUM.Icon(
iconRes = R.drawable.ic_arrow_down_24,
tintReference = {
TangemTheme.colors.icon.accent
},
)
},
title = resourceReference(R.string.tangempay_topup_receive_title), title = resourceReference(R.string.tangempay_topup_receive_title),
description = resourceReference(R.string.tangempay_topup_receive_body), description = resourceReference(R.string.tangempay_topup_receive_body),
onClick = { listener.onClickReceive(value) }, onClick = { listener.onClickReceive(value) },

View file

@ -1,313 +0,0 @@
package com.tangem.features.tangempay.model.transformers
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.price
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.ButtonState
import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM
import com.tangem.utils.StringsSigns
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.isPositive
import com.tangem.utils.extensions.isZero
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
internal object TangemPayTxHistoryDetailsConverter :
Converter<TangemPayTxHistoryDetailsConverter.Input, TangemPayTxHistoryDetailsUM> {
private val dateFormatter = DateTimeFormatters.getBestFormatterBySkeleton("dd MMMM")
private val paySpendSubtitleConverter = PayDetailsSpendSubtitleConverter
override fun convert(value: Input): TangemPayTxHistoryDetailsUM {
val transaction = value.item
return TangemPayTxHistoryDetailsUM(
isBalanceHidden = value.isBalanceHidden,
title = transaction.extractDate(),
iconState = transaction.extractIcon(),
transactionTitle = transaction.extractTransactionTitle(),
transactionSubtitle = transaction.extractTransactionSubtitle(),
transactionAmount = transaction.extractAmount(),
transactionAmountColor = transaction.extractAmountColor(),
localTransactionText = transaction.extractLocalAmount(),
labelState = transaction.extractLabel(),
notification = transaction.extractNotification(),
buttons = value.extractButtonsState(),
dismiss = value.onDismiss,
)
}
private fun TangemPayTxHistoryItem.extractDate(): TextReference {
val date = DateTimeFormatters.formatDate(this.date, dateFormatter)
val time = DateTimeFormatters.formatDate(this.date, DateTimeFormatters.timeFormatter)
return stringReference("$date ${StringsSigns.DOT} $time")
}
private fun TangemPayTxHistoryItem.extractIcon(): ImageReference {
return when (this) {
is TangemPayTxHistoryItem.Collateral -> when (this.type) {
TangemPayTxHistoryItem.Type.Deposit -> ImageReference.Res(R.drawable.ic_arrow_down_24)
TangemPayTxHistoryItem.Type.Withdrawal -> ImageReference.Res(R.drawable.ic_arrow_up_24)
}
is TangemPayTxHistoryItem.Fee -> ImageReference.Res(R.drawable.ic_percent_24)
is TangemPayTxHistoryItem.Payment -> ImageReference.Res(R.drawable.ic_arrow_up_24)
is TangemPayTxHistoryItem.Spend -> {
val merchantIcon = this.enrichedMerchantIconUrl
if (!merchantIcon.isNullOrEmpty()) {
ImageReference.Url(merchantIcon)
} else {
ImageReference.Res(R.drawable.ic_category_24)
}
}
}
}
private fun TangemPayTxHistoryItem.extractTransactionTitle(): TextReference {
return when (this) {
is TangemPayTxHistoryItem.Spend -> stringReference(this.enrichedMerchantName ?: this.merchantName)
is TangemPayTxHistoryItem.Payment -> resourceReference(R.string.tangem_pay_withdrawal)
is TangemPayTxHistoryItem.Collateral -> when (this.type) {
TangemPayTxHistoryItem.Type.Deposit -> resourceReference(R.string.tangem_pay_deposit)
TangemPayTxHistoryItem.Type.Withdrawal -> resourceReference(R.string.tangem_pay_withdrawal)
}
is TangemPayTxHistoryItem.Fee -> {
this.description?.let(::stringReference) ?: resourceReference(R.string.tangem_pay_fee_title)
}
}
}
private fun TangemPayTxHistoryItem.extractTransactionSubtitle(): TextReference {
return when (this) {
is TangemPayTxHistoryItem.Fee -> resourceReference(R.string.tangem_pay_fee_subtitle)
is TangemPayTxHistoryItem.Payment -> resourceReference(R.string.common_transfer)
is TangemPayTxHistoryItem.Collateral -> resourceReference(R.string.common_transfer)
is TangemPayTxHistoryItem.Spend -> paySpendSubtitleConverter.convert(this)
}
}
private fun TangemPayTxHistoryItem.extractAmount(): String {
return when (this) {
is TangemPayTxHistoryItem.Fee,
is TangemPayTxHistoryItem.Payment,
-> {
val amount = this.amount.format {
fiat(
fiatCurrencyCode = this@extractAmount.currency.currencyCode,
fiatCurrencySymbol = this@extractAmount.currency.symbol,
)
}
StringsSigns.MINUS + amount
}
is TangemPayTxHistoryItem.Spend -> {
val amountPrefix = when {
this.amount.isZero() -> ""
this.status == TangemPayTxHistoryItem.Status.REVERSED -> StringsSigns.MINUS
this.status == TangemPayTxHistoryItem.Status.DECLINED ||
this.amount.isPositive() -> StringsSigns.MINUS
else -> StringsSigns.PLUS
}
val amount = when (this.status) {
TangemPayTxHistoryItem.Status.DECLINED -> this.authorizedAmount
else -> this.amount
}
val formattedAmount = amount.abs().format {
fiat(
fiatCurrencyCode = this@extractAmount.currency.currencyCode,
fiatCurrencySymbol = this@extractAmount.currency.symbol,
)
}
amountPrefix + formattedAmount
}
is TangemPayTxHistoryItem.Collateral -> {
val amountPrefix = when {
this.amount.isZero() -> ""
this.amount.isPositive() -> StringsSigns.PLUS
else -> StringsSigns.MINUS
}
val amount = this.amount.abs().format {
fiat(
fiatCurrencyCode = this@extractAmount.currency.currencyCode,
fiatCurrencySymbol = this@extractAmount.currency.symbol,
)
}
amountPrefix + amount
}
}
}
private fun TangemPayTxHistoryItem.extractAmountColor(): ColorReference {
return when (this) {
is TangemPayTxHistoryItem.Fee,
is TangemPayTxHistoryItem.Payment,
-> themedColor { TangemTheme.colors.text.primary1 }
is TangemPayTxHistoryItem.Collateral -> when (this.type) {
TangemPayTxHistoryItem.Type.Deposit -> themedColor { TangemTheme.colors.text.accent }
TangemPayTxHistoryItem.Type.Withdrawal -> themedColor { TangemTheme.colors.text.primary1 }
}
is TangemPayTxHistoryItem.Spend -> when (this.status) {
TangemPayTxHistoryItem.Status.DECLINED -> themedColor { TangemTheme.colors.text.warning }
else -> themedColor { TangemTheme.colors.text.primary1 }
}
}
}
private fun TangemPayTxHistoryItem.extractLocalAmount(): String? {
return when (this) {
is TangemPayTxHistoryItem.Collateral,
is TangemPayTxHistoryItem.Fee,
is TangemPayTxHistoryItem.Payment,
-> null
is TangemPayTxHistoryItem.Spend -> {
val localCurrency = this.localCurrency
val localAmount = this.localAmount
if (localCurrency != null && localAmount != null && localCurrency != currency) {
val amountPrefix = when {
localAmount.isZero() -> ""
this.status == TangemPayTxHistoryItem.Status.DECLINED ||
localAmount.isPositive() -> StringsSigns.MINUS
else -> StringsSigns.PLUS
}
val amount = localAmount.abs().format {
fiat(
fiatCurrencyCode = localCurrency.currencyCode,
fiatCurrencySymbol = localCurrency.symbol,
).price()
}
amountPrefix + amount
} else {
null
}
}
}
}
private fun TangemPayTxHistoryItem.extractLabel(): LabelUM? {
return when (this) {
is TangemPayTxHistoryItem.Fee,
is TangemPayTxHistoryItem.Payment,
is TangemPayTxHistoryItem.Collateral,
-> null
is TangemPayTxHistoryItem.Spend -> when (this.status) {
TangemPayTxHistoryItem.Status.COMPLETED -> LabelUM(
text = resourceReference(R.string.tangem_pay_status_completed),
style = LabelStyle.ACCENT,
)
TangemPayTxHistoryItem.Status.PENDING -> LabelUM(
text = resourceReference(R.string.tangem_pay_status_pending),
style = LabelStyle.REGULAR,
icon = com.tangem.core.ui.R.drawable.ic_clock_24,
)
TangemPayTxHistoryItem.Status.DECLINED -> LabelUM(
text = resourceReference(R.string.tangem_pay_status_declined),
style = LabelStyle.WARNING,
)
TangemPayTxHistoryItem.Status.REVERSED -> LabelUM(
text = resourceReference(R.string.tangem_pay_status_reversed),
style = LabelStyle.REGULAR,
)
TangemPayTxHistoryItem.Status.RESERVED,
TangemPayTxHistoryItem.Status.UNKNOWN,
-> null
}
}
}
private fun TangemPayTxHistoryItem.extractNotification(): TangemPayTxHistoryDetailsUM.NotificationState? {
return when (this) {
is TangemPayTxHistoryItem.Payment -> null
is TangemPayTxHistoryItem.Collateral -> null
is TangemPayTxHistoryItem.Fee -> TangemPayTxHistoryDetailsUM.NotificationState(
config = NotificationConfig(
title = resourceReference(R.string.tangem_pay_transaction_fee_notification_text),
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
),
titleColor = themedColor { TangemTheme.colors.text.tertiary },
iconTint = themedColor { TangemTheme.colors.icon.secondary },
containerColor = null,
)
is TangemPayTxHistoryItem.Spend -> when (this.status) {
TangemPayTxHistoryItem.Status.DECLINED -> TangemPayTxHistoryDetailsUM.NotificationState(
config = NotificationConfig(
title = declinedReason.let { reason ->
if (reason.isNullOrEmpty()) {
resourceReference(R.string.tangem_pay_transaction_declined_notification_text)
} else {
resourceReference(
id = R.string.tangem_pay_history_item_spend_mc_declined_reason,
formatArgs = wrappedList(TangemPayDeclinedReasonMapper.map(reason)),
)
}
},
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
),
titleColor = themedColor { TangemTheme.colors.text.warning },
iconTint = themedColor { TangemTheme.colors.icon.warning },
containerColor = themedColor { TangemColorPalette.Amaranth.copy(alpha = 0.1F) },
)
TangemPayTxHistoryItem.Status.REVERSED -> TangemPayTxHistoryDetailsUM.NotificationState(
config = NotificationConfig(
title = resourceReference(R.string.tangem_pay_transaction_reversed_notification_text),
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
),
titleColor = themedColor { TangemTheme.colors.text.tertiary },
iconTint = themedColor { TangemTheme.colors.icon.secondary },
containerColor = null,
)
TangemPayTxHistoryItem.Status.PENDING,
TangemPayTxHistoryItem.Status.COMPLETED,
TangemPayTxHistoryItem.Status.RESERVED,
TangemPayTxHistoryItem.Status.UNKNOWN,
-> null
}
}
}
private fun Input.extractButtonsState(): ImmutableList<ButtonState> {
return when (this.item) {
is TangemPayTxHistoryItem.Fee -> persistentListOf(
ButtonState(
text = resourceReference(R.string.tangem_pay_get_help),
onClick = this.onDisputeClick,
),
)
is TangemPayTxHistoryItem.Spend -> persistentListOf(
ButtonState(
text = resourceReference(R.string.tangem_pay_get_help),
onClick = this.onDisputeClick,
),
)
is TangemPayTxHistoryItem.Payment -> persistentListOf(
ButtonState(
text = resourceReference(R.string.tangem_pay_get_help),
onClick = this.onDisputeClick,
),
)
is TangemPayTxHistoryItem.Collateral -> persistentListOf(
ButtonState(
text = resourceReference(R.string.tangem_pay_get_help),
onClick = this.onDisputeClick,
),
)
}
}
data class Input(
val item: TangemPayTxHistoryItem,
val isBalanceHidden: Boolean,
val onExplorerClick: (String?) -> Unit,
val onDisputeClick: () -> Unit,
val onDismiss: () -> Unit,
)
}

View file

@ -1,162 +0,0 @@
package com.tangem.features.tangempay.ui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.ds.image.TangemIcon
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.TextReference.Res
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayAddFundsItemUM
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun TangemPayAddFundsContent(state: TangemPayAddFundsUM) {
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = state.dismiss,
content = TangemBottomSheetConfigContent.Empty,
),
onBack = state.dismiss,
containerColor = TangemTheme.colors.background.tertiary,
title = {
TangemModalBottomSheetTitle(
title = resourceReference(R.string.tangempay_card_details_add_funds),
endIconRes = R.drawable.ic_close_24,
onEndClick = state.dismiss,
)
},
) {
if (state.errorMessage != null) {
MessageBottomSheetContent(state.errorMessage)
} else {
Surface(
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
shape = TangemTheme.shapes.roundedCornersXMedium,
color = TangemTheme.colors.background.primary,
) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
) {
Text(
modifier = Modifier.padding(top = 12.dp, start = 16.dp, end = 16.dp),
text = resourceReference(R.string.tangempay_card_details_add_funds_subtitle).resolveReference(),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.subtitle2,
)
state.items.fastForEach {
key(it.title) {
TangemPayTopUpItem(state = it)
}
}
}
}
SpacerH16()
}
}
}
@Composable
private fun TangemPayTopUpItem(state: TangemPayAddFundsItemUM, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable(onClick = state.onClick)
.padding(horizontal = 12.dp, vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.background(
color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f),
shape = CircleShape,
)
.size(36.dp),
) {
TangemIcon(
modifier = Modifier.size(16.dp),
tangemIconUM = state.icon,
)
}
Column(
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
Text(
text = state.title.resolveReference(),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
)
Text(
text = state.description.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
}
}
@Preview(showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun TangemPayAddFundsContentPreview() {
TangemThemePreview {
TangemPayAddFundsContent(
state = TangemPayAddFundsUM(
items = persistentListOf(
TangemPayAddFundsItemUM(
icon = TangemIconUM.Icon(
iconRes = R.drawable.ic_exchange_vertical_24,
tintReference = {
TangemTheme.colors.icon.accent
},
),
title = Res(R.string.common_exchange),
description = Res(R.string.exсhange_token_description),
onClick = {},
),
TangemPayAddFundsItemUM(
icon = TangemIconUM.Icon(
iconRes = R.drawable.ic_arrow_down_24,
tintReference = {
TangemTheme.colors.icon.accent
},
),
title = Res(R.string.common_receive),
description = Res(R.string.receive_token_description),
onClick = {},
),
),
dismiss = {},
errorMessage = null,
),
)
}
}

View file

@ -1,120 +1,19 @@
package com.tangem.features.tangempay.ui package com.tangem.features.tangempay.ui
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.ds.message.TangemMessage import com.tangem.core.ui.ds.message.TangemMessage
import com.tangem.core.ui.ds.message.TangemMessageEffect import com.tangem.core.ui.ds.message.TangemMessageEffect
import com.tangem.core.ui.extensions.clickableSingle import com.tangem.core.ui.extensions.clickableSingle
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.LocalVisaRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.AddToWalletBlockState import com.tangem.features.tangempay.entity.AddToWalletBlockState
private const val GRADIENT_START_COLOR = 0xFF252934
private const val GRADIENT_END_COLOR = 0xFF12141E
private const val GRADIENT_OFFSET_X = 0f
private const val GRADIENT_OFFSET_Y = 80F
private const val GRADIENT_RADIUS = 200F
@Composable @Composable
internal fun TangemPayAddToWalletBlock(state: AddToWalletBlockState, modifier: Modifier = Modifier) { internal fun TangemPayAddToWalletBlock(state: AddToWalletBlockState, modifier: Modifier = Modifier) {
if (LocalVisaRedesignEnabled.current) {
TangemPayAddToWalletBlockV2(state = state, modifier = modifier)
} else {
TangemPayAddToWalletBlockV1(state = state, modifier = modifier)
}
}
@Composable
private fun TangemPayAddToWalletBlockV1(state: AddToWalletBlockState, modifier: Modifier = Modifier) {
Box(
modifier = modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(
brush = Brush.radialGradient(
colors = listOf(Color(GRADIENT_START_COLOR), Color(GRADIENT_END_COLOR)),
center = Offset(GRADIENT_OFFSET_X, GRADIENT_OFFSET_Y),
radius = GRADIENT_RADIUS,
),
)
.clickable(onClick = state.onClick),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Image(
painter = painterResource(R.drawable.img_google_wallet_48),
contentDescription = null,
modifier = Modifier
.size(36.dp)
.clip(CircleShape),
)
Spacer(Modifier.width(12.dp))
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.Center,
) {
Text(
text = stringResourceSafe(R.string.tangempay_card_details_open_wallet_notification_title),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.constantWhite,
)
Spacer(Modifier.height(6.dp))
Text(
text = stringResourceSafe(R.string.tangempay_card_details_open_wallet_notification_subtitle),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
Box(
modifier = Modifier.fillMaxHeight(),
contentAlignment = Alignment.TopEnd,
) {
Image(
modifier = Modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.clickable(onClick = state.onClickClose)
.padding(4.dp)
.size(12.dp),
painter = painterResource(id = R.drawable.ic_close_24),
colorFilter = ColorFilter.tint(TangemTheme.colors.icon.inactive),
contentDescription = null,
)
}
}
}
}
@Composable
private fun TangemPayAddToWalletBlockV2(state: AddToWalletBlockState, modifier: Modifier = Modifier) {
TangemMessage( TangemMessage(
modifier = modifier.clickableSingle(onClick = state.onClick), modifier = modifier.clickableSingle(onClick = state.onClick),
onCloseClick = state.onClickClose, onCloseClick = state.onClickClose,

View file

@ -1,193 +0,0 @@
package com.tangem.features.tangempay.ui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
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.domain.models.pay.TangemPayCardFrozenState
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayAddToWalletStepItemUM
import com.tangem.features.tangempay.entity.TangemPayAddToWalletUM
import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun TangemPayAddToWalletScreen(
state: TangemPayAddToWalletUM,
cardDetailsState: TangemPayCardDetailsUM,
modifier: Modifier = Modifier,
) {
val listState = rememberLazyListState()
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
Column(
modifier = modifier
.fillMaxSize()
.navigationBarsPadding(),
) {
AppBarWithBackButton(
modifier = Modifier.statusBarsPadding(),
iconRes = R.drawable.ic_close_24,
onBackClick = state.onBackClick,
)
LazyColumn(
modifier = Modifier
.fillMaxSize()
.weight(1f),
state = listState,
contentPadding = PaddingValues(bottom = TangemTheme.dimens.spacing16 + bottomBarHeight),
) {
item(TangemPayCardDetailsUM::class.java) {
TangemPayCard(
state = cardDetailsState,
modifier = Modifier.padding(horizontal = 16.dp).padding(top = 8.dp),
)
}
item(key = TangemPayAddToWalletUM::class.java) {
Text(
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 24.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.tangempay_card_details_open_wallet_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
}
itemsIndexed(
items = state.steps,
key = { _, step -> "${TangemPayAddToWalletStepItemUM::class.java}${step.count}" },
) { idx, step ->
StepItem(
modifier = Modifier.padding(bottom = if (idx < state.steps.lastIndex) 16.dp else 0.dp),
stepNumber = step.count,
title = step.text,
)
}
}
SecondaryButton(
modifier = Modifier
.imePadding()
.padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
.fillMaxWidth(),
text = resourceReference(R.string.common_got_it).resolveReference(),
onClick = state.onBackClick,
)
if (state.showAddToWalletButton) {
PrimaryButton(
modifier = Modifier
.imePadding()
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = resourceReference(R.string.tangempay_card_details_open_wallet_button).resolveReference(),
onClick = state.onClickOpenWallet,
)
}
}
}
@Composable
private fun StepItem(stepNumber: Int, title: TextReference, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.background(color = Color.Transparent, shape = TangemTheme.shapes.roundedCorners8)
.padding(horizontal = 24.dp),
verticalAlignment = Alignment.Top,
) {
Box(
modifier = Modifier
.size(24.dp)
.background(color = TangemColorPalette.Azure, shape = CircleShape),
contentAlignment = Alignment.Center,
) {
Text(
text = stepNumber.toString(),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary2,
)
}
Spacer(modifier = Modifier.width(12.dp))
Text(
text = title.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
}
}
@Preview(showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PreviewTangemPayAddToWalletScreen() {
TangemThemePreview {
TangemPayAddToWalletScreen(
state = TangemPayAddToWalletUM(
steps = persistentListOf(
TangemPayAddToWalletStepItemUM(
count = 1,
text = resourceReference(R.string.tangempay_card_details_open_wallet_step_1),
),
TangemPayAddToWalletStepItemUM(
count = 2,
text = resourceReference(R.string.tangempay_card_details_open_wallet_step_2),
),
TangemPayAddToWalletStepItemUM(
count = 3,
text = resourceReference(R.string.tangempay_card_details_open_wallet_step_3),
),
TangemPayAddToWalletStepItemUM(
count = 4,
text = resourceReference(R.string.tangempay_card_details_open_wallet_step_4),
),
TangemPayAddToWalletStepItemUM(
count = 5,
text = resourceReference(R.string.tangempay_card_details_open_wallet_step_5),
),
),
showAddToWalletButton = true,
onBackClick = {},
onClickOpenWallet = {},
),
cardDetailsState = TangemPayCardDetailsUM(
number = "",
numberShort = "*1245",
expiry = "••/••",
cvv = "•••",
onCopy = { _, _ -> },
onClick = {},
buttonText = TextReference.Res(R.string.tangempay_card_details_hide_text),
cardFrozenState = TangemPayCardFrozenState.Unfrozen,
displayNameState = null,
cardImageUrl = null,
),
)
}
}

View file

@ -21,7 +21,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
@ -57,7 +56,6 @@ import com.tangem.core.ui.components.buttons.common.TangemButtonSize
import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds2.button.TangemButton import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.extensions.conditional import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.conditionalCompose
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.* import com.tangem.core.ui.res.*
@ -118,7 +116,6 @@ internal fun TangemPayCard(state: TangemPayCardDetailsUM, modifier: Modifier = M
@Suppress("LongMethod", "DestructuringDeclarationWithTooManyEntries") @Suppress("LongMethod", "DestructuringDeclarationWithTooManyEntries")
@Composable @Composable
private fun TangemPayCardDetailsHiddenBlock(state: TangemPayCardDetailsUM, modifier: Modifier = Modifier) { private fun TangemPayCardDetailsHiddenBlock(state: TangemPayCardDetailsUM, modifier: Modifier = Modifier) {
val isRedesignEnabled = LocalVisaRedesignEnabled.current
Box(modifier = modifier.fillMaxSize()) { Box(modifier = modifier.fillMaxSize()) {
TangemPayCardBackground( TangemPayCardBackground(
modifier = Modifier modifier = Modifier
@ -163,24 +160,7 @@ private fun TangemPayCardDetailsHiddenBlock(state: TangemPayCardDetailsUM, modif
) )
when (state.cardFrozenState) { when (state.cardFrozenState) {
TangemPayCardFrozenState.Frozen -> { TangemPayCardFrozenState.Frozen -> Unit
if (!isRedesignEnabled) {
Icon(
modifier = Modifier
.constrainAs(frozenIconRef) {
start.linkTo(cardNumberRef.end, margin = 4.dp)
top.linkTo(cardNumberRef.top)
bottom.linkTo(cardNumberRef.bottom)
}
.padding(bottom = 8.dp)
.size(16.dp)
.testTag(TangemPayTestTags.CARD_FROZEN_BADGE),
painter = painterResource(id = R.drawable.ic_snow_24),
contentDescription = null,
tint = TangemTheme.colors.icon.constant,
)
}
}
TangemPayCardFrozenState.Pending -> CircularProgressIndicator( TangemPayCardFrozenState.Pending -> CircularProgressIndicator(
modifier = Modifier modifier = Modifier
.constrainAs(frozenIconRef) { .constrainAs(frozenIconRef) {
@ -203,9 +183,7 @@ private fun TangemPayCardDetailsHiddenBlock(state: TangemPayCardDetailsUM, modif
bottom.linkTo(parent.bottom) bottom.linkTo(parent.bottom)
} }
.testTag(TangemPayTestTags.CARD_DETAILS_SHOW_BUTTON), .testTag(TangemPayTestTags.CARD_DETAILS_SHOW_BUTTON),
visible = !isRedesignEnabled || visible = state.isLoading || state.shouldShowCardDetailsButtonOnCard,
state.isLoading ||
state.shouldShowCardDetailsButtonOnCard,
) { ) {
TangemPayCardDetailsCustomButton( TangemPayCardDetailsCustomButton(
text = stringResourceSafe(id = R.string.tangempay_card_details_show_details), text = stringResourceSafe(id = R.string.tangempay_card_details_show_details),
@ -286,8 +264,7 @@ private fun CardBgWrapper(
back: @Composable () -> Unit, back: @Composable () -> Unit,
front: @Composable () -> Unit, front: @Composable () -> Unit,
) { ) {
val isRedesignEnabled = LocalVisaRedesignEnabled.current val shouldShowDetailsBg = shouldShowDetails
val shouldShowDetailsBg = shouldShowDetails && isRedesignEnabled
Box( Box(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
@ -295,32 +272,13 @@ private fun CardBgWrapper(
rotationY = rotateCardY rotationY = rotateCardY
cameraDistance = zAxisDistance cameraDistance = zAxisDistance
} }
.conditionalCompose( .clip(RoundedCornerShape(TangemTheme.dimens2.x5))
condition = isRedesignEnabled, .border(
modifier = { width = 1.dp,
clip(RoundedCornerShape(TangemTheme.dimens2.x5)) shape = RoundedCornerShape(TangemTheme.dimens2.x5),
.border( color = TangemTheme.colors3.border.secondary,
width = 1.dp, )
shape = RoundedCornerShape(TangemTheme.dimens2.x5), .background(CardBackgroundColor),
color = TangemTheme.colors3.border.secondary,
)
.background(CardBackgroundColor)
},
otherModifier = {
clip(RoundedCornerShape(16.dp))
.background(CardBackgroundColor)
.border(
width = 1.dp,
brush = Brush.linearGradient(
colors = listOf(
TangemTheme.colors.text.constantWhite.copy(alpha = 0.1F),
TangemTheme.colors.text.constantWhite.copy(alpha = 0f),
),
),
shape = RoundedCornerShape(16.dp),
)
},
),
) { ) {
EqualHeightCardSides( EqualHeightCardSides(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -400,30 +358,17 @@ private fun CardTopBlock(modifier: Modifier = Modifier) {
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),
) { ) {
if (LocalVisaRedesignEnabled.current) { Text(
Text( text = stringResourceSafe(R.string.tangempay_digital_card),
text = stringResourceSafe(R.string.tangempay_digital_card), style = TangemTheme.typography3.body.medium,
style = TangemTheme.typography3.body.medium, color = TangemTheme.colors3.text.staticDark.primary,
color = TangemTheme.colors3.text.staticDark.primary, )
) Icon(
Icon( modifier = Modifier.size(TangemTheme.dimens2.x5),
modifier = Modifier.size(TangemTheme.dimens2.x5), imageVector = ImageVector.vectorResource(R.drawable.ic_cloud_fill_16),
imageVector = ImageVector.vectorResource(R.drawable.ic_cloud_fill_16), tint = TangemTheme.colors3.icon.staticDark,
tint = TangemTheme.colors3.icon.staticDark, contentDescription = null,
contentDescription = null, )
)
} else {
Icon(
painter = painterResource(R.drawable.ic_cloud_fill_16),
tint = TangemTheme.colors.icon.constant,
contentDescription = null,
)
Text(
text = stringResourceSafe(R.string.tangempay_digital_card),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.constantWhite,
)
}
} }
} }
@ -434,36 +379,22 @@ private fun ConstraintLayoutScope.CardNumberBlock(
cardNumberRef: ConstrainedLayoutReference, cardNumberRef: ConstrainedLayoutReference,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
if (LocalVisaRedesignEnabled.current) { Text(
Text( text = numberShort,
text = numberShort, style = TangemTheme.typography3.body.medium,
style = TangemTheme.typography3.body.medium, color = if (isRenaming) {
color = if (isRenaming) { TangemTheme.colors3.text.staticDark.secondary
TangemTheme.colors3.text.staticDark.secondary } else {
} else { TangemTheme.colors3.text.staticDark.primary
TangemTheme.colors3.text.staticDark.primary },
}, modifier = modifier
modifier = modifier .constrainAs(cardNumberRef) {
.constrainAs(cardNumberRef) { start.linkTo(parent.start)
start.linkTo(parent.start) bottom.linkTo(parent.bottom)
bottom.linkTo(parent.bottom) }
} .padding(bottom = 8.dp)
.padding(bottom = 8.dp) .testTag(TangemPayTestTags.CARD_NUMBER_SHORT),
.testTag(TangemPayTestTags.CARD_NUMBER_SHORT), )
)
} else {
Text(
text = numberShort,
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.constantWhite,
modifier = modifier
.constrainAs(cardNumberRef) {
start.linkTo(parent.start)
bottom.linkTo(parent.bottom)
}
.padding(bottom = 8.dp),
)
}
} }
@Composable @Composable
@ -476,67 +407,37 @@ private fun CardDisplayName(state: DisplayNameState, modifier: Modifier = Modifi
@Composable @Composable
private fun DisplayOnlyCardDisplayName(state: DisplayNameState.Display, modifier: Modifier = Modifier) { private fun DisplayOnlyCardDisplayName(state: DisplayNameState.Display, modifier: Modifier = Modifier) {
if (LocalVisaRedesignEnabled.current) { Row(
Row( modifier = modifier.conditional(
modifier = modifier.conditional( condition = state.isEditingEnabled,
condition = state.isEditingEnabled, modifier = { clickable(onClick = state.onClick) },
modifier = { clickable(onClick = state.onClick) }, ),
), horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1), verticalAlignment = Alignment.CenterVertically,
verticalAlignment = Alignment.CenterVertically, ) {
) { Text(
Text( text = state.displayName,
text = state.displayName, style = TangemTheme.typography3.body.medium,
style = TangemTheme.typography3.body.medium, color = TangemTheme.colors3.text.staticDark.secondary,
color = TangemTheme.colors3.text.staticDark.secondary, maxLines = 1,
maxLines = 1, )
if (state.isEditingEnabled) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_edit_card_20),
contentDescription = null,
modifier = Modifier.size(TangemTheme.dimens2.x5),
tint = TangemTheme.colors3.icon.staticDark,
) )
if (state.isEditingEnabled) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_edit_card_20),
contentDescription = null,
modifier = Modifier.size(TangemTheme.dimens2.x5),
tint = TangemTheme.colors3.icon.staticDark,
)
}
}
} else {
Row(
modifier = modifier.conditional(
condition = state.isEditingEnabled,
modifier = { clickable(onClick = state.onClick) },
),
horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = state.displayName,
style = TangemTheme.typography.caption1.copy(color = TangemTheme.colors.text.constantWhite),
maxLines = 1,
)
if (state.isEditingEnabled) {
Icon(
painter = painterResource(id = R.drawable.ic_edit_new_12),
contentDescription = null,
modifier = Modifier.size(10.dp),
tint = TangemTheme.colors.text.constantWhite,
)
}
} }
} }
} }
@Composable @Composable
private fun EditingCardDisplayName(state: DisplayNameState.Editing, modifier: Modifier = Modifier) { private fun EditingCardDisplayName(state: DisplayNameState.Editing, modifier: Modifier = Modifier) {
val isRedesignEnabled = LocalVisaRedesignEnabled.current
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val placeholder = stringResourceSafe(R.string.tangempay_card_edit_name_placeholder) val placeholder = stringResourceSafe(R.string.tangempay_card_edit_name_placeholder)
val textStyle = if (isRedesignEnabled) { val textStyle = TangemTheme.typography3.body.medium.copy(color = TangemTheme.colors3.text.staticDark.primary)
TangemTheme.typography3.body.medium.copy(color = TangemTheme.colors3.text.staticDark.primary)
} else {
TangemTheme.typography.caption1.copy(color = TangemTheme.colors.text.constantWhite)
}
val textMeasurer = rememberTextMeasurer() val textMeasurer = rememberTextMeasurer()
val measuredText = state.editingValue.text.ifEmpty { placeholder } val measuredText = state.editingValue.text.ifEmpty { placeholder }
val textWidthDp = with(LocalDensity.current) { val textWidthDp = with(LocalDensity.current) {
@ -551,11 +452,7 @@ private fun EditingCardDisplayName(state: DisplayNameState.Editing, modifier: Mo
.focusRequester(focusRequester), .focusRequester(focusRequester),
textStyle = textStyle, textStyle = textStyle,
singleLine = true, singleLine = true,
cursorBrush = if (isRedesignEnabled) { cursorBrush = SolidColor(TangemTheme.colors3.text.staticDark.primary),
SolidColor(TangemTheme.colors3.text.staticDark.primary)
} else {
SolidColor(TangemTheme.colors.text.constantWhite)
},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions( keyboardActions = KeyboardActions(
onDone = if (state.isSubmitEnabled) { onDone = if (state.isSubmitEnabled) {
@ -569,13 +466,7 @@ private fun EditingCardDisplayName(state: DisplayNameState.Editing, modifier: Mo
if (state.editingValue.text.isEmpty()) { if (state.editingValue.text.isEmpty()) {
Text( Text(
text = placeholder, text = placeholder,
style = textStyle.copy( style = textStyle.copy(color = TangemTheme.colors3.text.staticDark.secondary),
color = if (isRedesignEnabled) {
TangemTheme.colors3.text.staticDark.secondary
} else {
TangemTheme.colors.text.tertiary
},
),
) )
} }
innerTextField() innerTextField()
@ -640,30 +531,19 @@ private fun TangemPayCardDetailsShownBlock(
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Row { Row {
SpacerWMax() SpacerWMax()
if (LocalVisaRedesignEnabled.current) { // Must use dark theme locally for button cause card is dark
// Must use dark theme locally for button cause card is dark CompositionLocalProvider(LocalIsInDarkTheme provides true) {
CompositionLocalProvider(LocalIsInDarkTheme provides true) { TangemThemeRedesign {
TangemThemeRedesign { TangemButton(
TangemButton( modifier = Modifier
modifier = Modifier .padding(end = 16.dp, bottom = 8.dp)
.padding(end = 16.dp, bottom = 8.dp) .testTag(TangemPayTestTags.CARD_DETAILS_HIDE_BUTTON),
.testTag(TangemPayTestTags.CARD_DETAILS_HIDE_BUTTON), variant = TangemButton.Variant.Material,
variant = TangemButton.Variant.Material, size = TangemButton.Size.X8,
size = TangemButton.Size.X8, onClick = onHideDetails,
onClick = onHideDetails, iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_close_20),
iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_close_20), )
)
}
} }
} else {
TangemPayCardDetailsCustomButton(
modifier = Modifier
.padding(end = 16.dp, bottom = 8.dp)
.testTag(TangemPayTestTags.CARD_DETAILS_HIDE_BUTTON),
text = stringResourceSafe(id = R.string.tangempay_card_details_hide_details),
onClick = onHideDetails,
showProgress = false,
)
} }
} }
} }

View file

@ -6,8 +6,6 @@ import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyItemScope
@ -16,7 +14,6 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -25,9 +22,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
import com.tangem.core.ui.ds.TangemPagerIndicator import com.tangem.core.ui.ds.TangemPagerIndicator
import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.topbar.TangemTopBar import com.tangem.core.ui.ds.topbar.TangemTopBar
@ -74,7 +69,6 @@ private fun TangemPayCardPageScreen(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
cardSection: @Composable () -> Unit, cardSection: @Composable () -> Unit,
) { ) {
val isRedesignEnabled = LocalVisaRedesignEnabled.current
Scaffold( Scaffold(
modifier = modifier, modifier = modifier,
topBar = { topBar = {
@ -84,15 +78,11 @@ private fun TangemPayCardPageScreen(
) )
}, },
contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.navigationBars), contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.navigationBars),
containerColor = if (isRedesignEnabled) { containerColor = TangemTheme.colors3.bg.primary,
TangemTheme.colors3.bg.primary
} else {
TangemTheme.colors.background.secondary
},
) { scaffoldPaddings -> ) { scaffoldPaddings ->
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() } val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
val contentBottomPadding = TangemTheme.dimens.spacing16 + bottomBarHeight val contentBottomPadding = TangemTheme.dimens.spacing16 + bottomBarHeight
val reissueTitle = reissueTitleOrNull(isRedesignEnabled = isRedesignEnabled, cardState = state.cardState) val reissueTitle = reissueTitleOrNull(cardState = state.cardState)
if (reissueTitle != null) { if (reissueTitle != null) {
ReissueCardLayout( ReissueCardLayout(
@ -109,9 +99,7 @@ private fun TangemPayCardPageScreen(
.fillMaxSize() .fillMaxSize()
.padding(scaffoldPaddings), .padding(scaffoldPaddings),
contentPadding = PaddingValues(bottom = contentBottomPadding), contentPadding = PaddingValues(bottom = contentBottomPadding),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(0.dp),
if (isRedesignEnabled) 0.dp else TangemTheme.dimens.spacing16,
),
) { ) {
item(key = "Card") { item(key = "Card") {
Box(modifier = Modifier.padding(top = TangemTheme.dimens.spacing8)) { Box(modifier = Modifier.padding(top = TangemTheme.dimens.spacing8)) {
@ -119,7 +107,6 @@ private fun TangemPayCardPageScreen(
} }
} }
if ( if (
isRedesignEnabled &&
state.settingsV2.isNotEmpty() && state.settingsV2.isNotEmpty() &&
state.cardState == TangemPayCardState.Active state.cardState == TangemPayCardState.Active
) { ) {
@ -136,8 +123,7 @@ private fun TangemPayCardPageScreen(
} }
} }
private fun reissueTitleOrNull(isRedesignEnabled: Boolean, cardState: TangemPayCardState): TextReference? { private fun reissueTitleOrNull(cardState: TangemPayCardState): TextReference? {
if (!isRedesignEnabled) return null
return when (cardState) { return when (cardState) {
TangemPayCardState.Reissuing -> combinedReference( TangemPayCardState.Reissuing -> combinedReference(
resourceReference(R.string.tangempay_reissue_card_in_progress), resourceReference(R.string.tangempay_reissue_card_in_progress),
@ -243,95 +229,14 @@ private fun CardDetailsPage(controller: TangemPayCardDetailsController, modifier
} }
private fun LazyListScope.cardState(state: TangemPayCardPageUM) { private fun LazyListScope.cardState(state: TangemPayCardPageUM) {
when (state.cardState) { if (state.cardState != TangemPayCardState.Active) return
TangemPayCardState.Active -> { if (state.addToWalletBlockState != null) {
if (state.addToWalletBlockState != null) { cardPageItem(key = "GooglePay") {
cardPageItem(key = "GooglePay") { TangemPayAddToWalletBlock(state = state.addToWalletBlockState)
TangemPayAddToWalletBlock(state = state.addToWalletBlockState)
}
}
cardPageItem(key = "Limit") {
TangemPayDailyLimitBlock(state = state.dailyLimitState)
}
if (state.dailyLimitState is TangemPayDailyLimitBlockState.Error) {
cardPageItem(key = "LimitError") {
TangemPayDailyLimitErrorBlock()
}
}
cardPageItem(key = "Settings") {
TangemPayCardPageSettingsBlock(settings = state.settings)
}
}
TangemPayCardState.Reissuing -> cardPageItem(key = "Reissue") {
TangemPayReplacingCardBlock()
}
TangemPayCardState.Closing -> cardPageItem(key = "Closing") {
TangemPayReplacingCardBlock(
title = resourceReference(R.string.tangempay_card_page_closing_banner_title),
subtitle = resourceReference(R.string.tangempay_card_page_closing_banner_description),
)
}
TangemPayCardState.Issuing -> cardPageItem(key = "Issuing") {
TangemPayReplacingCardBlock(
title = resourceReference(R.string.tangempay_issuing_new_digital_card_title),
subtitle = resourceReference(R.string.tangempay_reissue_card_in_progress_description),
)
} }
} }
} cardPageItem(key = "Limit") {
TangemPayDailyLimitBlock(state = state.dailyLimitState)
@Composable
private fun TangemPayCardPageSettingsBlock(
settings: ImmutableList<TangemPayCardPageSetting>,
modifier: Modifier = Modifier,
) {
if (LocalVisaRedesignEnabled.current) return
Column(
modifier = modifier
.fillMaxWidth()
.background(
color = TangemTheme.colors.background.primary,
shape = TangemTheme.shapes.roundedCornersMedium,
),
) {
Text(
modifier = Modifier.padding(
start = TangemTheme.dimens.spacing12,
top = TangemTheme.dimens.spacing12,
bottom = TangemTheme.dimens.spacing4,
),
text = stringResourceSafe(R.string.tangempay_card_page_settings_title),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
settings.fastForEach { item ->
TangemPayCardPageSettingRow(
item = item,
onClick = item.onSettingClick,
)
}
}
}
@Composable
private fun TangemPayCardPageSettingRow(
item: TangemPayCardPageSetting,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(TangemTheme.dimens.spacing12)
.then(if (item.testTag != null) Modifier.testTag(item.testTag) else Modifier),
contentAlignment = Alignment.CenterStart,
) {
Text(
text = item.title.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
} }
} }
@ -341,41 +246,34 @@ private fun CardPageTopBar(
items: ImmutableList<TangemPayDropDownItemUM>, items: ImmutableList<TangemPayDropDownItemUM>,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
if (LocalVisaRedesignEnabled.current) { var isDropdownMenuShown by rememberSaveable { mutableStateOf(false) }
var isDropdownMenuShown by rememberSaveable { mutableStateOf(false) } TangemTopBar(
TangemTopBar( modifier = modifier.statusBarsPadding(),
modifier = modifier.statusBarsPadding(), startContent = {
startContent = { TangemButton(
iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_arrow_back_28),
onClick = onBackClick,
size = TangemButton.Size.X11,
variant = TangemButton.Variant.Material,
)
},
endContent = {
Box {
TangemButton( TangemButton(
iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_arrow_back_28), modifier = Modifier.testTag(TangemPayTestTags.CARD_PAGE_MORE_BUTTON),
onClick = onBackClick, iconStart = TangemIconUM.Icon(iconRes = CoreUiR.drawable.ic_more_default_24),
onClick = { isDropdownMenuShown = true },
size = TangemButton.Size.X11, size = TangemButton.Size.X11,
variant = TangemButton.Variant.Material, variant = TangemButton.Variant.Material,
) )
}, PayContextMenuBlock(
endContent = { items = items,
Box { onMenuDismiss = { isDropdownMenuShown = false },
TangemButton( isDropdownMenuShown = isDropdownMenuShown,
modifier = Modifier.testTag(TangemPayTestTags.CARD_PAGE_MORE_BUTTON), )
iconStart = TangemIconUM.Icon(iconRes = CoreUiR.drawable.ic_more_default_24), }
onClick = { isDropdownMenuShown = true }, },
size = TangemButton.Size.X11, )
variant = TangemButton.Variant.Material,
)
PayContextMenuBlock(
items = items,
onMenuDismiss = { isDropdownMenuShown = false },
isDropdownMenuShown = isDropdownMenuShown,
)
}
},
)
} else {
AppBarWithBackButton(
modifier = modifier.statusBarsPadding(),
onBackClick = onBackClick,
)
}
} }
private fun LazyListScope.cardPageItem( private fun LazyListScope.cardPageItem(
@ -400,23 +298,6 @@ private fun LazyListScope.cardPageItem(
} }
} }
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun TangemPayCardPageScreenPreviewV1() {
TangemThemePreview {
TangemPayCardPageScreen(
state = TangemPayCardPageUM.stub(),
cardSection = {
TangemPayCard(
state = previewCardDetailsState(),
modifier = Modifier.padding(horizontal = 16.dp),
)
},
)
}
}
private fun previewCardDetailsState(): TangemPayCardDetailsUM = TangemPayCardDetailsUM( private fun previewCardDetailsState(): TangemPayCardDetailsUM = TangemPayCardDetailsUM(
number = "•••• •••• •••• 1245", number = "•••• •••• •••• 1245",
numberShort = "··1245", numberShort = "··1245",
@ -440,7 +321,6 @@ private fun TangemPayCardPageScreenPreviewV2() {
TangemThemePreviewRedesign { TangemThemePreviewRedesign {
CompositionLocalProvider( CompositionLocalProvider(
LocalRedesignEnabled provides true, LocalRedesignEnabled provides true,
LocalVisaRedesignEnabled provides true,
) { ) {
TangemPayCardPageScreen( TangemPayCardPageScreen(
state = TangemPayCardPageUM.stub(), state = TangemPayCardPageUM.stub(),

View file

@ -1,134 +0,0 @@
package com.tangem.features.tangempay.ui
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.animateLottieCompositionAsState
import com.airbnb.lottie.compose.rememberLottieComposition
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.appbar.TangemTopAppBar
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.TangemPayTestTags
import com.tangem.features.tangempay.details.impl.R
@Composable
internal fun TangemPayChangePinCodeSuccessScreen(onClick: () -> Unit, modifier: Modifier = Modifier) {
val composition by rememberLottieComposition(spec = LottieCompositionSpec.RawRes(R.raw.anim_confetti))
val progress by animateLottieCompositionAsState(composition)
var showConfetti by remember { mutableStateOf(false) }
Column(
modifier = modifier
.fillMaxSize()
.navigationBarsPadding(),
) {
TangemTopAppBar(
modifier = Modifier.statusBarsPadding(),
title = resourceReference(R.string.common_done).resolveReference(),
startButton = null,
titleAlignment = Alignment.CenterHorizontally,
)
Column(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SuccessContent(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
)
PrimaryButton(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 16.dp)
.navigationBarsPadding()
.testTag(TangemPayTestTags.PIN_DONE_BUTTON),
text = stringResourceSafe(R.string.common_done),
onClick = onClick,
)
}
if (showConfetti) {
FullScreen(notTouchable = true) {
LottieAnimation(
composition = composition,
progress = { progress },
)
}
}
LaunchedEffect(Unit) {
showConfetti = true
}
LaunchedEffect(progress == 1f) {
if (progress == 1f) {
showConfetti = false
}
}
}
}
@Composable
private fun SuccessContent(modifier: Modifier = Modifier) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_success_blue_76),
tint = Color.Unspecified,
contentDescription = null,
modifier = Modifier
.size(76.dp),
)
SpacerH32()
Text(
modifier = Modifier
.padding(horizontal = 32.dp)
.testTag(TangemPayTestTags.PIN_SUCCESS_TITLE),
text = stringResourceSafe(R.string.tangempay_card_details_change_pin_success_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
SpacerH12()
Text(
modifier = Modifier
.padding(horizontal = 32.dp)
.testTag(TangemPayTestTags.PIN_SUCCESS_DESCRIPTION),
text = stringResourceSafe(R.string.tangempay_card_details_change_pin_success_description),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
)
SpacerH(72.dp)
}
}
@Preview(showBackground = true)
@Composable
private fun Preview() {
TangemThemePreview {
TangemPayChangePinCodeSuccessScreen(onClick = {})
}
}

View file

@ -1,192 +0,0 @@
package com.tangem.features.tangempay.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color.Companion.Transparent
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.navigationButtons.NavigationButton
import com.tangem.common.ui.navigationButtons.NavigationPrimaryButton
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.components.SpacerH4
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
import com.tangem.core.ui.extensions.resolveReference
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.test.TangemPayTestTags
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayChangePinUM
import kotlinx.coroutines.delay
@Composable
internal fun TangemPayChangePinScreen(
state: TangemPayChangePinUM,
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxSize()
.navigationBarsPadding(),
) {
AppBarWithBackButton(
modifier = Modifier.statusBarsPadding(),
onBackClick = onBackClick,
)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = 48.dp)
.padding(horizontal = 36.dp)
.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResourceSafe(R.string.visa_onboarding_pin_code_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
modifier = Modifier.testTag(TangemPayTestTags.PIN_SCREEN_TITLE),
)
SpacerH16()
Text(
text = stringResourceSafe(R.string.visa_onboarding_pin_code_description),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
modifier = Modifier.testTag(TangemPayTestTags.PIN_SCREEN_DESCRIPTION),
)
SpacerH(26.dp)
PinCodeSection(state)
}
NavigationPrimaryButton(
modifier = Modifier
.imePadding()
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth()
.testTag(TangemPayTestTags.PIN_SUBMIT_BUTTON),
primaryButton = NavigationButton(
textReference = resourceReference(R.string.common_submit),
onClick = state.onSubmitClick,
shouldShowProgress = state.submitButtonLoading,
isEnabled = state.submitButtonEnabled,
),
)
}
}
@Composable
private fun PinCodeSection(state: TangemPayChangePinUM, modifier: Modifier = Modifier) {
val focusRequester = remember { FocusRequester() }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
PinCode(
modifier = modifier,
value = state.pinCode,
onValueChange = state.onPinCodeChange,
focusRequester = focusRequester,
)
AnimatedVisibility(
visible = state.error != null,
) {
val error = remember(this) { requireNotNull(state.error) }
Column {
SpacerH4()
Text(
text = error.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.warning,
textAlign = TextAlign.Center,
modifier = Modifier.testTag(TangemPayTestTags.PIN_ERROR_MESSAGE),
)
}
}
}
LaunchedEffect(Unit) {
delay(timeMillis = 300)
focusRequester.requestFocus()
}
}
@Composable
private fun PinCode(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
numbersCount: Int = 4,
focusRequester: FocusRequester = remember { FocusRequester() },
) {
val keyboardController = LocalSoftwareKeyboardController.current
BasicTextField(
value = value,
onValueChange = { text ->
if (text.length <= numbersCount) {
onValueChange(text)
}
},
modifier = modifier
.focusRequester(focusRequester)
.clickable {
focusRequester.requestFocus()
keyboardController?.show()
}
.testTag(TangemPayTestTags.PIN_INPUT_FIELD),
textStyle = TangemTheme.typography.h1.copy(color = Transparent),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.NumberPassword,
imeAction = ImeAction.Done,
),
keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }),
cursorBrush = SolidColor(Transparent),
decorationBox = { innerTextField ->
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
repeat(numbersCount) { index ->
val digit = value.getOrNull(index)?.toString()
PinDigitBox(
digit = digit,
backgroundColor = TangemTheme.colors.field.focused,
borderColor = TangemTheme.colors.stroke.primary,
textColor = TangemTheme.colors.text.primary1,
textStyle = TangemTheme.typography.h1,
)
}
}
innerTextField()
}
},
)
}

View file

@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
@ -16,15 +15,10 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.layoutId import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.* import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.TangemRowContainer import com.tangem.core.ui.ds.row.TangemRowContainer
import com.tangem.core.ui.ds.row.TangemRowLayoutId import com.tangem.core.ui.ds.row.TangemRowLayoutId
@ -41,92 +35,7 @@ import com.tangem.features.tangempay.entity.TangemPayDailyLimitBlockState
@Composable @Composable
internal fun TangemPayDailyLimitBlock(state: TangemPayDailyLimitBlockState, modifier: Modifier = Modifier) { internal fun TangemPayDailyLimitBlock(state: TangemPayDailyLimitBlockState, modifier: Modifier = Modifier) {
if (LocalVisaRedesignEnabled.current) { CurrentLimitBlockV2(state, modifier.padding(top = TangemTheme.dimens2.x2))
CurrentLimitBlockV2(state, modifier.padding(top = TangemTheme.dimens2.x2))
} else {
TangemPayDailyLimitBlockV1(state, modifier)
}
}
@Composable
private fun TangemPayDailyLimitBlockV1(state: TangemPayDailyLimitBlockState, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxWidth()
.background(
color = TangemTheme.colors.background.primary,
shape = TangemTheme.shapes.roundedCornersMedium,
)
.padding(TangemTheme.dimens.spacing12),
) {
Text(
text = stringResourceSafe(R.string.tangempay_card_page_daily_limit_title),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
SpacerH4()
CurrentLimitBlockV1(state)
}
}
@Composable
private fun CurrentLimitBlockV1(state: TangemPayDailyLimitBlockState) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier
.size(36.dp)
.background(
color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f),
shape = CircleShape,
),
contentAlignment = Alignment.Center,
) {
Icon(
painter = painterResource(R.drawable.ic_limit_20),
contentDescription = null,
tint = TangemTheme.colors.icon.accent,
)
}
SpacerW12()
Column(
modifier = Modifier.weight(1f),
) {
Text(
text = stringResourceSafe(R.string.tangempay_card_page_daily_limit_current_limit),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
when (state) {
is TangemPayDailyLimitBlockState.Error,
is TangemPayDailyLimitBlockState.Content,
-> Text(
text = if (state is TangemPayDailyLimitBlockState.Content) state.limit else "",
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
TangemPayDailyLimitBlockState.Loading -> TextShimmer(
style = TangemTheme.typography.subtitle1,
text = "$50,000",
)
}
}
SpacerW8()
if (state is TangemPayDailyLimitBlockState.Content) {
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
SecondaryButton(
modifier = Modifier,
text = stringResourceSafe(R.string.tangempay_card_page_daily_limit_change),
onClick = state.onChangeClick,
size = TangemButtonSize.Small,
)
}
}
}
} }
@Composable @Composable
@ -279,36 +188,6 @@ private fun SubtitleLimit(state: TangemPayDailyLimitBlockState, modifier: Modifi
} }
} }
@Composable
internal fun TangemPayDailyLimitErrorBlock(modifier: Modifier = Modifier) {
if (LocalVisaRedesignEnabled.current) return
Notification(
config = NotificationConfig(
title = resourceReference(R.string.tangempay_card_page_daily_limit_error_title),
subtitle = resourceReference(R.string.tangempay_card_page_daily_limit_error_description),
iconResId = R.drawable.img_attention_20,
),
containerColor = TangemTheme.colors.background.action,
modifier = modifier.fillMaxWidth(),
)
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun Preview() {
TangemThemePreview {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Content.stub())
TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Error(onReloadClick = {}))
TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Loading)
TangemPayDailyLimitErrorBlock()
}
}
}
@Preview @Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
@ -316,7 +195,6 @@ private fun PreviewV2() {
TangemThemePreviewRedesign { TangemThemePreviewRedesign {
CompositionLocalProvider( CompositionLocalProvider(
LocalRedesignEnabled provides true, LocalRedesignEnabled provides true,
LocalVisaRedesignEnabled provides true,
) { ) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp),
@ -324,7 +202,6 @@ private fun PreviewV2() {
TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Content.stub()) TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Content.stub())
TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Error(onReloadClick = {})) TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Error(onReloadClick = {}))
TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Loading) TangemPayDailyLimitBlock(state = TangemPayDailyLimitBlockState.Loading)
TangemPayDailyLimitErrorBlock()
} }
} }
} }

View file

@ -0,0 +1,140 @@
package com.tangem.features.tangempay.ui
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.styledStringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.tangempay.components.txHistory.PreviewTangemPayTxHistoryComponent
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.AddToWalletBlockState
import com.tangem.features.tangempay.entity.CardsProgressBannerUM
import com.tangem.features.tangempay.entity.TangemPayAction
import com.tangem.features.tangempay.entity.TangemPayActionButtonUM
import com.tangem.features.tangempay.entity.TangemPayCardUiState
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarConfig
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
import kotlinx.collections.immutable.persistentListOf
internal class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<TangemPayDetailsUM>(
collection = listOf(
TangemPayDetailsUM(
topBarConfig = TangemPayDetailsTopBarConfig(
onBackClick = {},
onOpenMenu = {},
items = persistentListOf(),
itemsV2 = persistentListOf(),
),
pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = {}),
balanceBlockState = TangemPayDetailsBalanceBlockState.Content(
actionButtons = persistentListOf(
TangemPayActionButtonUM(
action = TangemPayAction.AddFunds,
config = ActionButtonConfig(
text = resourceReference(id = R.string.common_receive),
iconResId = R.drawable.ic_arrow_down_24,
onClick = {},
),
),
),
fiatBalance = combinedReference(
stringReference("1,234"),
styledStringReference(
".56",
{ SpanStyle(color = TangemTheme.colors3.text.primary) },
),
stringReference(" $"),
),
isBalanceFlickering = false,
cardsBlockState = TangemPayDetailsBalanceBlockState.CardsBlockState(
cards = persistentListOf(
TangemPayDetailsBalanceBlockState.Card(
lastDigits = "1234",
imageUrl = null,
onClick = {},
isEnabled = false,
isFrozen = false,
state = TangemPayCardUiState.Active,
),
TangemPayDetailsBalanceBlockState.Card(
lastDigits = "3456",
imageUrl = null,
onClick = {},
isEnabled = true,
isFrozen = false,
state = TangemPayCardUiState.Active,
),
),
onAddCardClick = {},
isAddCardEnabled = true,
),
isNegative = false,
isInactive = false,
),
isBalanceHidden = false,
addToWalletBlockState = AddToWalletBlockState(
onClick = {},
onClickClose = {},
),
accountDeactivatedNotificationConfig = null,
errorNotificationConfig = null,
),
TangemPayDetailsUM(
topBarConfig = TangemPayDetailsTopBarConfig(
onBackClick = {},
onOpenMenu = {},
items = persistentListOf(),
itemsV2 = persistentListOf(),
),
pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = {}),
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(
actionButtons = persistentListOf(),
cardsBlockState = TangemPayDetailsBalanceBlockState.CardsBlockState(
cards = persistentListOf(
TangemPayDetailsBalanceBlockState.Card(
lastDigits = "1234",
imageUrl = null,
onClick = {},
isFrozen = false,
isEnabled = true,
state = TangemPayCardUiState.InProgress,
),
),
onAddCardClick = {},
isAddCardEnabled = true,
progressBanner = CardsProgressBannerUM.Reissuing,
),
),
isBalanceHidden = false,
addToWalletBlockState = null,
accountDeactivatedNotificationConfig = null,
errorNotificationConfig = NotificationConfig(
title = TextReference.Str("Error title"),
subtitle = TextReference.Str("Error subtitle"),
iconResId = R.drawable.ic_alert_circle_24,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = TextReference.Str("Error btn text"),
onClick = {},
iconResId = R.drawable.ic_tangem_24,
),
),
),
),
)
internal class TangemPayDetailsTxHistoryProvider : CollectionPreviewParameterProvider<TangemPayTxHistoryUM>(
collection = listOf(
PreviewTangemPayTxHistoryComponent.loadingUM,
PreviewTangemPayTxHistoryComponent.contentUM,
PreviewTangemPayTxHistoryComponent.emptyUM,
PreviewTangemPayTxHistoryComponent.errorUM,
),
)

View file

@ -1,587 +0,0 @@
package com.tangem.features.tangempay.ui
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.tooling.preview.Devices
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 androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastForEach
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SpacerH12
import com.tangem.core.ui.components.buttons.HorizontalActionChips
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
import com.tangem.core.ui.components.buttons.small.TangemIconButton
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.components.text.applyBladeBrush
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.TangemPayTestTags
import com.tangem.core.ui.test.TokenDetailsTopBarTestTags
import com.tangem.core.ui.test.WalletConnectBottomSheetTestTags
import com.tangem.features.tangempay.components.express.PreviewEmptyExpressTransactionsComponent
import com.tangem.features.tangempay.components.txHistory.PreviewTangemPayTxHistoryComponent
import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryComponent
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.*
import com.tangem.features.tokendetails.ExpressTransactionsComponent
import com.tangem.utils.StringsSigns.DASH_SIGN
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
private const val DISABLED_ALPHA = 0.5f
@Suppress("LongMethod")
@Composable
internal fun TangemPayDetailsScreen(
state: TangemPayDetailsUM,
txHistoryComponent: TangemPayTxHistoryComponent,
expressTransactionsComponent: ExpressTransactionsComponent,
modifier: Modifier = Modifier,
) {
Scaffold(
modifier = modifier,
topBar = { TangemPayDetailsTopAppBar(config = state.topBarConfig) },
contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.navigationBars),
containerColor = TangemTheme.colors.background.secondary,
) { scaffoldPaddings ->
val listState = rememberLazyListState()
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
val txHistoryState by txHistoryComponent.state.collectAsStateWithLifecycle()
val expressState by expressTransactionsComponent.state.collectAsStateWithLifecycle()
val expressTransactionsBottomSheetState = expressState.bottomSheetSlot
TangemPullToRefreshContainer(
config = state.pullToRefreshConfig,
modifier = Modifier.padding(scaffoldPaddings),
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState,
contentPadding = PaddingValues(
bottom = TangemTheme.dimens.spacing16 + bottomBarHeight,
),
) {
item(key = "title") {
TangemPayTitle(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing16)
.padding(top = 4.dp)
.fillMaxWidth(),
)
}
if (state.errorNotificationConfig != null) {
item(
key = "error_message",
content = {
Notification(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing16)
.padding(top = 12.dp)
.fillMaxWidth(),
config = state.errorNotificationConfig,
)
},
)
}
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,
)
SpacerH12()
},
)
val progressBanner = state.balanceBlockState.cardsBlockState?.progressBanner
if (progressBanner == CardsProgressBannerUM.Reissuing) {
item(
key = "REISSUE_MESSAGE",
content = {
TangemPayReplacingCardBlock(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
)
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) {
expressTransactionsContentLegacy(
state = expressState.transactionsToDisplay,
modifier = modifier
.padding(horizontal = 16.dp)
.padding(top = 12.dp)
.fillMaxWidth(),
)
}
with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryState) }
}
}
}
expressTransactionsBottomSheetState?.content(null)
}
}
@Composable
private fun TangemPayTitle(modifier: Modifier = Modifier) {
Column(modifier = modifier) {
Text(
text = stringResourceSafe(R.string.tangempay_payment_account),
style = TangemTheme.typography.head,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
)
TangemPaySubtitle()
}
}
@Suppress("MagicNumber")
@Composable
private fun TangemPaySubtitle(modifier: Modifier = Modifier) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Box(modifier = Modifier.wrapContentWidth()) {
Icon(
painter = painterResource(id = R.drawable.ic_polygon_22),
contentDescription = null,
tint = TangemTheme.colors.text.constantWhite,
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing12)
.border(width = 2.dp, color = TangemTheme.colors.background.secondary, shape = CircleShape)
.padding(2.dp)
.clip(CircleShape)
.background(Color(0xFF8247E5))
.size(16.dp)
.testTag(WalletConnectBottomSheetTestTags.NETWORKS_ICONS),
)
Image(
painter = painterResource(id = R.drawable.img_usdc_16),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.border(width = 2.dp, color = TangemTheme.colors.background.secondary, shape = CircleShape)
.padding(2.dp)
.clip(CircleShape)
.size(16.dp)
.testTag(WalletConnectBottomSheetTestTags.NETWORKS_ICONS),
)
}
Text(
modifier = Modifier.align(Alignment.CenterVertically),
text = stringResourceSafe(R.string.tangempay_usdc_on_polygon_network),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
)
}
}
// region Balance block
@Composable
private fun TangemPayDetailsBalanceBlock(
state: TangemPayDetailsBalanceBlockState,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.background(
color = TangemTheme.colors.background.primary,
shape = TangemTheme.shapes.roundedCornersMedium,
)
.padding(vertical = 12.dp),
) {
Text(
modifier = Modifier.padding(start = 12.dp),
text = stringResourceSafe(R.string.common_balance_title),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.subtitle2,
)
FiatBalance(
modifier = Modifier.padding(start = 12.dp, top = 8.dp),
state = state,
isBalanceHidden = isBalanceHidden,
)
state.cardsBlockState?.let { cardsBlockState ->
CardsBlockRow(
modifier = Modifier
.wrapContentSize()
.padding(horizontal = 12.dp, vertical = 8.dp),
cardsBlockState = cardsBlockState,
)
}
if (state.actionButtons.isNotEmpty()) {
HorizontalActionChips(
modifier = Modifier.padding(top = 12.dp),
buttons = state.actionButtons.map { it.config }.toImmutableList(),
containerColor = TangemTheme.colors.background.primary,
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing12),
)
}
}
}
@Composable
private fun CardsBlockRow(
cardsBlockState: TangemPayDetailsBalanceBlockState.CardsBlockState,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
val itemsModifier = Modifier.size(width = 48.dp, height = 32.dp)
cardsBlockState.cards.fastForEach { card ->
TangemPayCardItem(modifier = itemsModifier, card = card)
}
TangemIconButton(
modifier = itemsModifier.alpha(if (cardsBlockState.isAddCardEnabled) 1f else DISABLED_ALPHA),
onClick = cardsBlockState.onAddCardClick,
iconRes = R.drawable.ic_plus_24,
shape = RoundedCornerShape(4.dp),
enabled = cardsBlockState.isAddCardEnabled,
)
}
}
@Composable
private fun TangemPayCardItem(card: TangemPayDetailsBalanceBlockState.Card, modifier: Modifier = Modifier) {
val isCardInProcess = card.state == TangemPayCardUiState.InProgress
Box(
modifier = modifier
.clip(RoundedCornerShape(4.dp))
.clickable(
onClick = card.onClick,
enabled = card.isEnabled,
)
.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_CARD_BUTTON),
) {
Image(
modifier = Modifier
.alpha(if (card.isEnabled) 1f else DISABLED_ALPHA)
.fillMaxSize(),
painter = painterResource(
if (isCardInProcess) {
R.drawable.img_visa_card_inactive_48_32
} else {
R.drawable.img_visa_card_48_32
},
),
contentDescription = null,
)
if (!isCardInProcess) {
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,
)
}
}
}
@Composable
private fun FiatBalance(
state: TangemPayDetailsBalanceBlockState,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
when (state) {
is TangemPayDetailsBalanceBlockState.Loading -> RectangleShimmer(
modifier = modifier.size(
width = TangemTheme.dimens.size102,
height = TangemTheme.dimens.size32,
),
)
is TangemPayDetailsBalanceBlockState.Content -> Text(
modifier = modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE),
text = state.fiatBalance.orMaskWithStars(isBalanceHidden).resolveReference(),
style = TangemTheme.typography.h2.applyBladeBrush(
isEnabled = state.isBalanceFlickering,
textColor = TangemTheme.colors.text.primary1,
),
)
is TangemPayDetailsBalanceBlockState.Error -> Text(
modifier = modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE),
text = DASH_SIGN.orMaskWithStars(isBalanceHidden),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
}
}
// endregion
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TangemPayDetailsTopAppBar(config: TangemPayDetailsTopBarConfig, modifier: Modifier = Modifier) {
var showDropdownMenu by rememberSaveable { mutableStateOf(false) }
TopAppBar(
modifier = modifier,
navigationIcon = {
IconButton(onClick = config.onBackClick) {
Icon(
painter = painterResource(id = R.drawable.ic_back_24),
tint = TangemTheme.colors.icon.primary1,
contentDescription = "Back",
)
}
},
title = {},
actions = {
AnimatedVisibility(visible = config.items.isNotEmpty()) {
IconButton(
onClick = {
config.onOpenMenu()
showDropdownMenu = true
},
) {
Icon(
painter = painterResource(id = R.drawable.ic_more_vertical_24),
tint = TangemTheme.colors.icon.primary1,
contentDescription = "More",
modifier = Modifier.testTag(TokenDetailsTopBarTestTags.MORE_BUTTON),
)
}
}
TangemDropdownMenu(
expanded = showDropdownMenu,
modifier = Modifier.background(TangemTheme.colors.background.primary),
onDismissRequest = { showDropdownMenu = false },
content = {
config.items.fastForEach { item ->
TangemDropdownItem(
item = item,
dismissParent = { showDropdownMenu = false },
)
}
},
)
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = TangemTheme.colors.background.secondary,
titleContentColor = TangemTheme.colors.icon.primary1,
actionIconContentColor = TangemTheme.colors.icon.primary1,
),
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(),
)
}
@Preview(device = Devices.PIXEL_7_PRO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_7_PRO)
@Composable
private fun TangemPayDetailsScreenPreview(
@PreviewParameter(TangemPayDetailsUMProvider::class) state: TangemPayDetailsUM,
) {
TangemThemePreview {
TangemPayDetailsScreen(
state = state,
txHistoryComponent = PreviewTangemPayTxHistoryComponent(
txHistoryUM = PreviewTangemPayTxHistoryComponent.contentUM,
),
expressTransactionsComponent = PreviewEmptyExpressTransactionsComponent(),
)
}
}
internal class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<TangemPayDetailsUM>(
collection = listOf(
TangemPayDetailsUM(
topBarConfig = TangemPayDetailsTopBarConfig(
onBackClick = {},
onOpenMenu = {},
items = persistentListOf(),
itemsV2 = persistentListOf(),
),
pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = {}),
balanceBlockState = TangemPayDetailsBalanceBlockState.Content(
actionButtons = persistentListOf(
TangemPayActionButtonUM(
action = TangemPayAction.AddFunds,
config = ActionButtonConfig(
text = resourceReference(id = R.string.common_receive),
iconResId = R.drawable.ic_arrow_down_24,
onClick = {},
),
),
),
fiatBalance = combinedReference(
stringReference("1,234"),
styledStringReference(
".56",
{ SpanStyle(color = TangemTheme.colors3.text.primary) },
),
stringReference(" $"),
),
isBalanceFlickering = false,
cardsBlockState = TangemPayDetailsBalanceBlockState.CardsBlockState(
cards = persistentListOf(
TangemPayDetailsBalanceBlockState.Card(
lastDigits = "1234",
imageUrl = null,
onClick = {},
isEnabled = false,
isFrozen = false,
state = TangemPayCardUiState.Active,
),
TangemPayDetailsBalanceBlockState.Card(
lastDigits = "3456",
imageUrl = null,
onClick = {},
isEnabled = true,
isFrozen = false,
state = TangemPayCardUiState.Active,
),
),
onAddCardClick = {},
isAddCardEnabled = true,
),
isNegative = false,
isInactive = false,
),
isBalanceHidden = false,
addToWalletBlockState = AddToWalletBlockState(
onClick = {},
onClickClose = {},
),
accountDeactivatedNotificationConfig = null,
errorNotificationConfig = null,
),
TangemPayDetailsUM(
topBarConfig = TangemPayDetailsTopBarConfig(
onBackClick = {},
onOpenMenu = {},
items = persistentListOf(),
itemsV2 = persistentListOf(),
),
pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = {}),
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(
actionButtons = persistentListOf(),
cardsBlockState = TangemPayDetailsBalanceBlockState.CardsBlockState(
cards = persistentListOf(
TangemPayDetailsBalanceBlockState.Card(
lastDigits = "1234",
imageUrl = null,
onClick = {},
isFrozen = false,
isEnabled = true,
state = TangemPayCardUiState.InProgress,
),
),
onAddCardClick = {},
isAddCardEnabled = true,
progressBanner = CardsProgressBannerUM.Reissuing,
),
),
isBalanceHidden = false,
addToWalletBlockState = null,
accountDeactivatedNotificationConfig = null,
errorNotificationConfig = NotificationConfig(
title = TextReference.Str("Error title"),
subtitle = TextReference.Str("Error subtitle"),
iconResId = R.drawable.ic_alert_circle_24,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = TextReference.Str("Error btn text"),
onClick = {},
iconResId = R.drawable.ic_tangem_24,
),
),
),
),
)
@Preview(device = Devices.PIXEL_7_PRO)
@Composable
private fun TangemPayDetailsTxHistoryScreenPreview(
@PreviewParameter(TangemPayDetailsTxHistoryProvider::class) state: TangemPayTxHistoryUM,
) {
TangemThemePreview {
TangemPayDetailsScreen(
state = TangemPayDetailsUMProvider().values.first(),
txHistoryComponent = PreviewTangemPayTxHistoryComponent(txHistoryUM = state),
expressTransactionsComponent = PreviewEmptyExpressTransactionsComponent(),
)
}
}
internal class TangemPayDetailsTxHistoryProvider : CollectionPreviewParameterProvider<TangemPayTxHistoryUM>(
collection = listOf(
PreviewTangemPayTxHistoryComponent.loadingUM,
PreviewTangemPayTxHistoryComponent.contentUM,
PreviewTangemPayTxHistoryComponent.emptyUM,
PreviewTangemPayTxHistoryComponent.errorUM,
),
)

View file

@ -4,18 +4,12 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.topbar.TangemTopBar import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds2.button.TangemButton import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.extensions.resourceReference 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.TangemTheme
import com.tangem.core.ui.res.generated.icons.Icons import com.tangem.core.ui.res.generated.icons.Icons
import com.tangem.core.ui.res.generated.icons.ic_cross_20 import com.tangem.core.ui.res.generated.icons.ic_cross_20
@ -23,80 +17,6 @@ import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM
import com.tangem.features.tangempay.entity.TangemPayEditDisplayNameUM import com.tangem.features.tangempay.entity.TangemPayEditDisplayNameUM
@Composable
internal fun TangemPayEditDisplayNameScreen(
isRedesignEnabled: Boolean,
state: TangemPayEditDisplayNameUM,
cardDetailsState: TangemPayCardDetailsUM,
modifier: Modifier = Modifier,
) {
if (isRedesignEnabled) {
TangemPayEditDisplayNameScreenV2(
state = state,
cardDetailsState = cardDetailsState,
modifier = modifier,
)
} else {
TangemPayEditDisplayNameScreenV1(
state = state,
cardDetailsState = cardDetailsState,
modifier = modifier,
)
}
}
@Composable
internal fun TangemPayEditDisplayNameScreenV1(
state: TangemPayEditDisplayNameUM,
cardDetailsState: TangemPayCardDetailsUM,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxSize()
.background(TangemTheme.colors.background.secondary)
.navigationBarsPadding(),
) {
Box(
modifier = Modifier
.statusBarsPadding()
.height(56.dp)
.fillMaxWidth(),
) {
IconButton(
modifier = Modifier.padding(start = 4.dp, top = 4.dp),
onClick = state.onDismiss,
) {
Icon(
painter = painterResource(id = com.tangem.core.ui.R.drawable.ic_close_24),
contentDescription = null,
tint = TangemTheme.colors.icon.primary1,
)
}
}
TangemPayCard(
state = cardDetailsState,
modifier = Modifier
.padding(horizontal = 16.dp)
.padding(top = 8.dp),
)
Spacer(modifier = Modifier.weight(1f))
PrimaryButton(
modifier = Modifier
.imePadding()
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.common_done),
onClick = state.onDoneClick,
showProgress = state.isLoading,
enabled = !state.isLoading && state.isDoneEnabled,
)
}
}
@Composable @Composable
internal fun TangemPayEditDisplayNameScreenV2( internal fun TangemPayEditDisplayNameScreenV2(
state: TangemPayEditDisplayNameUM, state: TangemPayEditDisplayNameUM,

View file

@ -1,218 +0,0 @@
package com.tangem.features.tangempay.ui
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.components.SpacerH24
import com.tangem.core.ui.components.SpacerH8
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.TextReference
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.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayReissueCardError
import com.tangem.features.tangempay.entity.TangemPayReissueCardUM
@Composable
internal fun TangemPayReissueCardContent(state: TangemPayReissueCardUM) {
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = state.onDismissRequest,
content = TangemBottomSheetConfigContent.Empty,
),
containerColor = TangemTheme.colors.background.tertiary,
onBack = state.onDismissRequest,
title = {
TangemModalBottomSheetTitle(
title = TextReference.EMPTY,
endIconRes = R.drawable.ic_close_24,
onEndClick = state.onDismissRequest,
)
},
) {
Content(state)
}
}
@Composable
private fun Content(state: TangemPayReissueCardUM) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = TangemTheme.dimens.spacing16),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Box(
modifier = Modifier
.size(TangemTheme.dimens.spacing56)
.clip(CircleShape)
.background(TangemTheme.colors.icon.accent.copy(alpha = 0.1f)),
contentAlignment = Alignment.Center,
) {
Icon(
painter = painterResource(id = R.drawable.ic_update_32),
contentDescription = null,
tint = TangemTheme.colors.icon.accent,
modifier = Modifier.size(32.dp),
)
}
SpacerH24()
Text(
text = stringResourceSafe(R.string.tangempay_reissue_card_title),
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
SpacerH8()
Text(
text = stringResourceSafe(R.string.tangempay_reissue_card_description),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
SpacerH24()
FeeBlock(state)
if (state.error != null) {
SpacerH16()
ErrorBlock(
error = state.error,
onRetryFee = state.onRetryFee,
onAddFundsClick = state.onAddFundsClick,
)
}
SpacerH24()
PrimaryButton(
modifier = Modifier.fillMaxWidth(),
text = stringResourceSafe(R.string.tangempay_reissue_card_confirm),
enabled = state.error == null && !state.isFeeLoading,
showProgress = state.isReissuingInProgress,
onClick = state.onConfirmClick,
)
SpacerH16()
}
}
@Composable
private fun FeeBlock(state: TangemPayReissueCardUM) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(
color = TangemTheme.colors.background.action,
shape = TangemTheme.shapes.roundedCornersMedium,
)
.padding(TangemTheme.dimens.spacing12),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResourceSafe(R.string.tangempay_reissue_card_fee_label),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
)
AnimatedContent(
targetState = when {
state.isFeeLoading -> null
state.error == TangemPayReissueCardError.InitialDataLoading -> ""
else -> state.feeAmount
},
) { fee ->
if (fee != null) {
Text(
text = fee,
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.tertiary,
)
} else {
TextShimmer(style = TangemTheme.typography.body1, text = "$0.00")
}
}
}
}
@Composable
private fun ErrorBlock(error: TangemPayReissueCardError, onAddFundsClick: () -> Unit, onRetryFee: () -> Unit) {
when (error) {
TangemPayReissueCardError.InsufficientFunds -> Notification(
config = NotificationConfig(
title = resourceReference(R.string.tangempay_reissue_card_insufficient_funds_title),
subtitle = resourceReference(R.string.tangempay_reissue_card_insufficient_funds_subtitle),
iconResId = R.drawable.img_usdc_16,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(R.string.tangempay_card_details_add_funds),
iconResId = R.drawable.ic_plus_24,
onClick = onAddFundsClick,
),
),
containerColor = TangemTheme.colors.background.action,
modifier = Modifier.fillMaxWidth(),
)
TangemPayReissueCardError.InitialDataLoading -> Notification(
config = NotificationConfig(
title = resourceReference(R.string.tangempay_reissue_card_fee_unreachable_error_title),
subtitle = resourceReference(R.string.send_fee_unreachable_error_text),
iconResId = R.drawable.img_attention_20,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(R.string.warning_button_refresh),
onClick = onRetryFee,
),
),
containerColor = TangemTheme.colors.background.action,
modifier = Modifier.fillMaxWidth(),
)
}
}
@Preview(showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun preview() = TangemThemePreview {
TangemPayReissueCardContent(
state = TangemPayReissueCardUM.stub(),
)
}

View file

@ -1,66 +0,0 @@
package com.tangem.features.tangempay.ui
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.SpacerH24
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.LocalVisaRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.features.tangempay.details.impl.R
@Composable
internal fun TangemPayReplacingCardBlock(
modifier: Modifier = Modifier,
title: TextReference? = resourceReference(R.string.tangempay_reissue_card_in_progress),
subtitle: TextReference = resourceReference(R.string.tangempay_reissue_card_in_progress_description),
) {
if (LocalVisaRedesignEnabled.current) {
return
} else {
Block(title = title, subtitle = subtitle, modifier = modifier)
}
}
@Composable
private fun Block(
modifier: Modifier = Modifier,
title: TextReference? = resourceReference(R.string.tangempay_reissue_card_in_progress),
subtitle: TextReference = resourceReference(R.string.tangempay_reissue_card_in_progress_description),
) {
Notification(
modifier = modifier,
config = NotificationConfig(
iconResId = R.drawable.ic_update_32,
iconTint = NotificationConfig.IconTint.Accent,
title = title,
subtitle = subtitle,
),
containerColor = TangemTheme.colors.background.primary,
)
}
@Preview(showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun Preview() {
Column {
TangemThemePreview {
TangemPayReplacingCardBlock()
}
SpacerH24()
CompositionLocalProvider(LocalVisaRedesignEnabled provides true) {
TangemThemePreviewRedesign {
TangemPayReplacingCardBlock()
}
}
}
}

View file

@ -1,408 +0,0 @@
package com.tangem.features.tangempay.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ChainStyle
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import com.tangem.core.ui.R
import com.tangem.core.ui.components.CircleShimmer
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.buttons.actions.ActionButton
import com.tangem.core.ui.components.list.InfiniteListHandler
import com.tangem.core.ui.components.transactions.TxHistoryGroupTitle
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.extensions.ImageReference
import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.EmptyTransactionBlockTestTags
import com.tangem.core.ui.test.TransactionHistoryBlockTestTags
import com.tangem.features.tangempay.entity.TangemPayEmptyTransactionHistoryState
import com.tangem.features.tangempay.entity.TangemPayTransactionState
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
private const val LOAD_ITEMS_BUFFER = 20
internal fun LazyListScope.tangemPayTxHistoryItems(listState: LazyListState, state: TangemPayTxHistoryUM) {
when (state) {
is TangemPayTxHistoryUM.Content -> contentItems(listState = listState, state = state)
is TangemPayTxHistoryUM.Empty -> nonContentItem(state = TangemPayEmptyTransactionHistoryState.Empty)
is TangemPayTxHistoryUM.Error -> nonContentItem(
state = TangemPayEmptyTransactionHistoryState.FailedToLoad(onReload = state.onReload),
)
is TangemPayTxHistoryUM.Loading -> loadingItems(state = state)
}
}
private fun LazyListScope.nonContentItem(state: TangemPayEmptyTransactionHistoryState, modifier: Modifier = Modifier) {
item(key = state::class.java, contentType = state::class.java) {
TangemPayEmptyTransactionBlock(
state = state,
modifier = modifier
.padding(horizontal = TangemTheme.dimens.spacing16, vertical = TangemTheme.dimens.spacing12)
.fillMaxWidth(),
)
}
}
private fun LazyListScope.contentItems(listState: LazyListState, state: TangemPayTxHistoryUM.Content) {
itemsIndexed(
items = state.items,
key = { index, item ->
when (item) {
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.GroupTitle -> item.itemKey
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Title -> index + item.hashCode()
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction ->
item.transaction.id + (item.transaction as? TangemPayTransactionState.Content)?.hashCode()
}
},
contentType = { _, item -> item::class.java },
itemContent = { index, item ->
TangemPayTxHistoryListItem(
state = item,
isBalanceHidden = state.isBalanceHidden,
modifier = Modifier.roundedShapeItemDecoration(
currentIndex = index,
lastIndex = state.items.lastIndex,
),
)
},
)
item {
InfiniteListHandler(
listState = listState,
buffer = LOAD_ITEMS_BUFFER,
onLoadMore = state.loadMore,
)
}
}
private fun LazyListScope.loadingItems(state: TangemPayTxHistoryUM.Loading) {
itemsIndexed(
items = state.items,
key = { _, item ->
when (item) {
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.GroupTitle -> item.itemKey
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Title -> item.hashCode()
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction ->
item.transaction.id + (item.transaction as? TangemPayTransactionState.Content)?.hashCode()
}
},
contentType = { _, item -> item::class.java },
itemContent = { index, item ->
TangemPayTxHistoryListItem(
state = item,
isBalanceHidden = true,
modifier = Modifier.roundedShapeItemDecoration(
currentIndex = index,
lastIndex = state.items.lastIndex,
),
)
},
)
}
@Composable
private fun TangemPayTxHistoryListItem(
state: TangemPayTxHistoryUM.TangemPayTxHistoryItemUM,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
when (state) {
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.GroupTitle -> {
if (state.isLoading) {
RectangleShimmer(
modifier = modifier
.fillMaxWidth()
.padding(
vertical = TangemTheme.dimens.spacing8,
horizontal = TangemTheme.dimens.spacing12,
)
.height(TangemTheme.dimens.size12),
)
} else {
TxHistoryGroupTitle(config = state.legacyGroupTitle, modifier = modifier)
}
}
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Title -> {
TangemPayTxHistoryTitle(modifier = modifier)
}
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction -> {
TangemPayTransaction(
transactionState = state.transaction,
isBalanceHidden = isBalanceHidden,
modifier = modifier,
)
}
}
}
@Composable
private fun TangemPayTxHistoryTitle(modifier: Modifier = Modifier) {
Text(
text = stringResourceSafe(id = R.string.common_transactions),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.subtitle2,
modifier = modifier
.background(TangemTheme.colors.background.primary)
.padding(top = TangemTheme.dimens.spacing12)
.padding(horizontal = TangemTheme.dimens.spacing12)
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size24)
.testTag(TransactionHistoryBlockTestTags.TITLE_TEXT),
)
}
@Suppress("LongMethod")
@Composable
private fun TangemPayTransaction(
transactionState: TangemPayTransactionState,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
Surface(
modifier = modifier
.background(TangemTheme.colors.background.primary)
.clickable(
enabled = transactionState is TangemPayTransactionState.Content,
onClick = (transactionState as? TangemPayTransactionState.Content)?.onClick ?: {},
)
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size56)
.padding(
vertical = TangemTheme.dimens.spacing8,
horizontal = TangemTheme.dimens.spacing12,
),
color = TangemTheme.colors.background.primary,
) {
@Suppress("DestructuringDeclarationWithTooManyEntries")
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
val (iconItem, titleItem, subtitleItem, amountItem, timestampItem) = createRefs()
createVerticalChain(titleItem, subtitleItem, chainStyle = ChainStyle.Spread)
createVerticalChain(amountItem, timestampItem, chainStyle = ChainStyle.Spread)
Icon(
state = transactionState,
modifier = Modifier
.size(TangemTheme.dimens.size40)
.constrainAs(iconItem) {
start.linkTo(parent.start)
centerVerticallyTo(parent)
},
)
Title(
state = transactionState,
modifier = Modifier
.padding(
start = TangemTheme.dimens.spacing12,
end = TangemTheme.dimens.spacing4,
)
.constrainAs(titleItem) {
top.linkTo(parent.top)
bottom.linkTo(subtitleItem.top)
start.linkTo(iconItem.end)
end.linkTo(amountItem.start)
width = Dimension.fillToConstraints
},
)
Subtitle(
state = transactionState,
modifier = Modifier
.padding(
start = TangemTheme.dimens.spacing12,
end = TangemTheme.dimens.spacing4,
)
.constrainAs(subtitleItem) {
top.linkTo(titleItem.bottom)
bottom.linkTo(parent.bottom)
start.linkTo(iconItem.end)
end.linkTo(timestampItem.start)
width = Dimension.fillToConstraints
},
)
Amount(
state = transactionState,
isBalanceHidden = isBalanceHidden,
modifier = Modifier.constrainAs(amountItem) {
top.linkTo(parent.top)
bottom.linkTo(timestampItem.top)
start.linkTo(titleItem.end)
end.linkTo(parent.end)
width = Dimension.preferredWrapContent
},
)
Timestamp(
state = transactionState,
modifier = Modifier.constrainAs(timestampItem) {
top.linkTo(amountItem.bottom)
bottom.linkTo(parent.bottom)
end.linkTo(parent.end)
},
)
}
}
}
@Composable
private fun Icon(state: TangemPayTransactionState, modifier: Modifier = Modifier) {
when (state) {
is TangemPayTransactionState.Content -> when (val iconReference = state.icon) {
is ImageReference.Url -> RemoteIcon(modifier = modifier, url = iconReference.url)
is ImageReference.Res -> LocalStaticIcon(
modifier = modifier,
id = iconReference.resId,
iconSize = TangemTheme.dimens.size20,
)
}
is TangemPayTransactionState.Loading -> CircleShimmer(modifier = modifier)
}
}
@Composable
private fun Title(state: TangemPayTransactionState, modifier: Modifier = Modifier) {
when (state) {
is TangemPayTransactionState.Content -> {
Text(
modifier = modifier,
text = state.title.resolveReference(),
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.subtitle2,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
is TangemPayTransactionState.Loading -> {
RectangleShimmer(
modifier = modifier.size(width = TangemTheme.dimens.size70, height = TangemTheme.dimens.size12),
)
}
}
}
@Composable
private fun Subtitle(state: TangemPayTransactionState, modifier: Modifier = Modifier) {
when (state) {
is TangemPayTransactionState.Content -> {
Text(
text = state.subtitle.resolveReference(),
modifier = modifier,
textAlign = TextAlign.Start,
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.caption2,
)
}
is TangemPayTransactionState.Loading -> {
RectangleShimmer(
modifier = modifier.size(width = TangemTheme.dimens.size52, height = TangemTheme.dimens.size12),
)
}
}
}
@Composable
private fun Amount(state: TangemPayTransactionState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
when (state) {
is TangemPayTransactionState.Content -> {
Text(
text = state.amount.orMaskWithStars(isBalanceHidden),
modifier = modifier,
textAlign = TextAlign.End,
color = state.amountColor.resolveReference(),
style = TangemTheme.typography.body2,
)
}
is TangemPayTransactionState.Loading -> {
RectangleShimmer(
modifier = modifier.size(width = TangemTheme.dimens.size72, height = TangemTheme.dimens.size12),
)
}
}
}
@Composable
private fun Timestamp(state: TangemPayTransactionState, modifier: Modifier = Modifier) {
when (state) {
is TangemPayTransactionState.Content -> {
Text(
text = state.time,
modifier = modifier,
textAlign = TextAlign.End,
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.caption2,
)
}
is TangemPayTransactionState.Loading -> {
RectangleShimmer(
modifier = modifier.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12),
)
}
}
}
@Composable
private fun TangemPayEmptyTransactionBlock(
state: TangemPayEmptyTransactionHistoryState,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(color = TangemTheme.colors.background.primary)
.padding(vertical = TangemTheme.dimens.spacing24)
.testTag(EmptyTransactionBlockTestTags.BLOCK),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
modifier = Modifier
.size(TangemTheme.dimens.size64)
.testTag(EmptyTransactionBlockTestTags.ICON),
painter = painterResource(id = state.iconRes),
tint = TangemTheme.colors.icon.inactive,
contentDescription = null,
)
Text(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing32)
.testTag(EmptyTransactionBlockTestTags.TEXT),
textAlign = TextAlign.Center,
text = state.text.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
when (state) {
is TangemPayEmptyTransactionHistoryState.Empty -> Unit
is TangemPayEmptyTransactionHistoryState.FailedToLoad -> ActionButton(
modifier = Modifier
.padding(horizontal = 24.dp)
.fillMaxWidth(),
config = state.actionButtonConfig,
)
}
}
}

View file

@ -1,204 +0,0 @@
package com.tangem.features.tangempay.ui
import android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color.Companion.Transparent
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.SpacerH12
import com.tangem.core.ui.components.SpacerH24
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayViewPinUM
@Composable
internal fun TangemPayViewPinContent(state: TangemPayViewPinUM) {
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = state.onDismiss,
content = TangemBottomSheetConfigContent.Empty,
),
onBack = state.onDismiss,
containerColor = TangemTheme.colors.background.tertiary,
title = {
TangemModalBottomSheetTitle(
title = TextReference.EMPTY,
endIconRes = R.drawable.ic_close_24,
onEndClick = state.onDismiss,
)
},
) {
when (state) {
is TangemPayViewPinUM.Content -> {
PinSuccessContent(state)
}
is TangemPayViewPinUM.Error -> {
MessageBottomSheetContent(state.errorMessage)
}
is TangemPayViewPinUM.Loading -> {
PinLoadingContent()
}
}
}
}
@Composable
private fun PinSuccessContent(state: TangemPayViewPinUM.Content, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResourceSafe(R.string.tangempay_card_details_view_pin_code_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
SpacerH12()
Text(
text = stringResourceSafe(R.string.tangempay_card_details_view_pin_code_description),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
)
SpacerH24()
Box(
modifier = Modifier
.fillMaxWidth()
.height(200.dp),
contentAlignment = Alignment.Center,
) {
PinCode(
modifier = Modifier
.align(Alignment.TopCenter),
value = state.pin,
)
SecondaryButton(
modifier = Modifier
.align(Alignment.BottomCenter)
.imePadding()
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.tangempay_change_pin_code),
onClick = state.onClickChangePin,
)
}
}
}
@Composable
private fun PinLoadingContent(modifier: Modifier = Modifier) {
Column(
modifier = modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResourceSafe(R.string.tangempay_card_details_view_pin_code_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
SpacerH12()
Text(
text = stringResourceSafe(R.string.tangempay_card_details_view_pin_code_description),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
)
SpacerH24()
Box(
modifier = Modifier
.fillMaxWidth()
.height(200.dp),
contentAlignment = Alignment.Center,
) {
CircularProgressIndicator(
color = TangemTheme.colors.text.disabled,
modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = 16.dp)
.size(TangemTheme.dimens.size24),
)
}
}
}
@Composable
private fun PinCode(value: String, modifier: Modifier = Modifier, numbersCount: Int = 4) {
BasicTextField(
value = value,
onValueChange = {},
modifier = modifier,
textStyle = TangemTheme.typography.h1.copy(color = Transparent),
cursorBrush = SolidColor(Transparent),
decorationBox = { innerTextField ->
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
repeat(numbersCount) { index ->
val digit = value.getOrNull(index)?.toString()
PinDigitBox(
digit = digit,
backgroundColor = TangemTheme.colors.background.action,
borderColor = TangemTheme.colors.stroke.primary,
textColor = TangemTheme.colors.text.primary1,
textStyle = TangemTheme.typography.h1,
)
}
}
innerTextField()
}
},
)
}
@Preview(showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun TangemPayViewPinContentPreview() {
TangemThemePreview {
TangemPayViewPinContent(
state = TangemPayViewPinUM.Content(
pin = "1234",
onClickChangePin = {},
onDismiss = {},
),
)
}
}

View file

@ -1,307 +0,0 @@
package com.tangem.features.tangempay.ui
import android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Devices
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 androidx.compose.ui.util.fastForEach
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.SecondaryButtonIconStart
import com.tangem.core.ui.components.SpacerH32
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.components.label.Label
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.ButtonState
import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@Suppress("LongMethod")
@Composable
internal fun TangemPayTxHistoryDetailsContent(state: TangemPayTxHistoryDetailsUM) {
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = state.dismiss,
content = TangemBottomSheetConfigContent.Empty,
),
onBack = state.dismiss,
containerColor = TangemTheme.colors.background.tertiary,
title = {
TangemModalBottomSheetTitle(
title = state.title,
endIconRes = R.drawable.ic_close_24,
onEndClick = state.dismiss,
)
},
) {
Column(
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
modifier = Modifier
.padding(top = 24.dp)
.size(88.dp),
iconState = state.iconState,
)
Text(
modifier = Modifier.padding(top = 32.dp),
text = state.transactionTitle.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
Text(
modifier = Modifier.padding(top = 2.dp),
text = state.transactionSubtitle.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.Center,
)
Text(
modifier = Modifier.padding(top = 8.dp),
text = state.transactionAmount.orMaskWithStars(state.isBalanceHidden),
style = TangemTheme.typography.head,
color = state.transactionAmountColor.resolveReference(),
)
if (state.localTransactionText != null) {
Text(
modifier = Modifier.padding(top = 4.dp),
text = state.localTransactionText.orMaskWithStars(state.isBalanceHidden),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
}
if (state.labelState != null) {
Label(state = state.labelState, modifier = Modifier.padding(top = 12.dp))
}
SpacerH32()
if (state.notification != null) {
Notification(
config = state.notification.config,
titleColor = state.notification.titleColor.resolveReference(),
iconTint = state.notification.iconTint.resolveReference(),
containerColor = state.notification.containerColor?.resolveReference(),
)
}
ButtonsContainer(
modifier = Modifier
.padding(vertical = 16.dp)
.fillMaxWidth(),
buttons = state.buttons,
)
}
}
}
@Composable
private fun ButtonsContainer(buttons: ImmutableList<ButtonState>, modifier: Modifier = Modifier) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
buttons.fastForEach { buttonState ->
if (buttonState.startIcon != null) {
SecondaryButtonIconStart(
modifier = Modifier.fillMaxWidth(),
text = buttonState.text.resolveReference(),
iconResId = buttonState.startIcon.resId,
onClick = buttonState.onClick,
)
} else {
SecondaryButton(
modifier = Modifier.fillMaxWidth(),
text = buttonState.text.resolveReference(),
onClick = buttonState.onClick,
)
}
}
}
}
@Composable
private fun Icon(iconState: ImageReference, modifier: Modifier = Modifier) {
when (iconState) {
is ImageReference.Res -> LocalStaticIcon(modifier = modifier, id = iconState.resId, iconSize = 40.dp)
is ImageReference.Url -> RemoteIcon(modifier = modifier, url = iconState.url)
}
}
@Preview(device = Devices.PIXEL_7_PRO)
@Preview(device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun TangemPayTxHistoryDetailsContentPreview(
@PreviewParameter(TangemPayTxHistoryDetailsUMProvider::class) state: TangemPayTxHistoryDetailsUM,
) {
TangemThemePreview {
TangemPayTxHistoryDetailsContent(state = state)
}
}
private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterProvider<TangemPayTxHistoryDetailsUM>(
listOf(
TangemPayTxHistoryDetailsUM(
isBalanceHidden = true,
title = stringReference("12 June • 12:40"),
iconState = ImageReference.Res(R.drawable.ic_category_24),
transactionTitle = stringReference("Starbucks"),
transactionSubtitle = stringReference("Food and drinks"),
transactionAmount = "-$5.86",
transactionAmountColor = themedColor { TangemTheme.colors.text.primary1 },
localTransactionText = null,
labelState = LabelUM(
text = resourceReference(R.string.tangem_pay_status_pending),
style = LabelStyle.REGULAR,
icon = R.drawable.ic_clock_24,
),
notification = null,
buttons = persistentListOf(
ButtonState(
text = resourceReference(R.string.tangem_pay_dispute),
onClick = {},
),
),
dismiss = {},
),
TangemPayTxHistoryDetailsUM(
isBalanceHidden = true,
title = stringReference("12 June • 12:40"),
iconState = ImageReference.Res(R.drawable.ic_category_24),
transactionTitle = stringReference("Starbucks"),
transactionSubtitle = stringReference("Food and drinks"),
transactionAmount = "-$5.86",
transactionAmountColor = themedColor { TangemTheme.colors.text.warning },
localTransactionText = null,
labelState = LabelUM(
text = resourceReference(R.string.tangem_pay_status_declined),
style = LabelStyle.WARNING,
),
notification = TangemPayTxHistoryDetailsUM.NotificationState(
config = NotificationConfig(
title = stringReference("The bank rejected this transaction request."),
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
),
titleColor = themedColor { TangemTheme.colors.text.warning },
iconTint = themedColor { TangemTheme.colors.icon.warning },
containerColor = themedColor { TangemColorPalette.Amaranth.copy(alpha = 0.1F) },
),
buttons = persistentListOf(
ButtonState(
text = resourceReference(R.string.tangem_pay_dispute),
onClick = {},
),
),
dismiss = {},
),
TangemPayTxHistoryDetailsUM(
isBalanceHidden = true,
title = stringReference("12 June • 12:40"),
iconState = ImageReference.Res(R.drawable.ic_category_24),
transactionTitle = stringReference("Starbucks"),
transactionSubtitle = stringReference("Food and drinks Food and drinks Food and drinks Food and drinks"),
transactionAmount = "-$5.86",
transactionAmountColor = themedColor { TangemTheme.colors.text.primary1 },
localTransactionText = "€ 5.36",
labelState = LabelUM(
text = resourceReference(R.string.tangem_pay_status_completed),
style = LabelStyle.ACCENT,
),
notification = null,
buttons = persistentListOf(
ButtonState(
text = resourceReference(R.string.tangem_pay_dispute),
onClick = {},
),
),
dismiss = {},
),
TangemPayTxHistoryDetailsUM(
isBalanceHidden = false,
title = stringReference("12 June • 12:40"),
iconState = ImageReference.Res(R.drawable.ic_percent_24),
transactionTitle = stringReference("Fee"),
transactionSubtitle = stringReference("Service fee"),
transactionAmount = "-$5.86",
transactionAmountColor = themedColor { TangemTheme.colors.text.primary1 },
localTransactionText = null,
labelState = null,
notification = TangemPayTxHistoryDetailsUM.NotificationState(
config = NotificationConfig(
title = stringReference("This fee goes to cover the cost of handling your transfer."),
subtitle = TextReference.EMPTY,
iconResId = R.drawable.ic_token_info_24,
),
titleColor = themedColor { TangemTheme.colors.text.tertiary },
iconTint = themedColor { TangemTheme.colors.icon.secondary },
containerColor = null,
),
buttons = persistentListOf(
ButtonState(
text = resourceReference(R.string.tangem_pay_dispute),
onClick = {},
),
),
dismiss = {},
),
TangemPayTxHistoryDetailsUM(
isBalanceHidden = false,
title = stringReference("12 June • 12:40"),
iconState = ImageReference.Res(R.drawable.ic_arrow_down_24),
transactionTitle = stringReference("Deposit"),
transactionSubtitle = stringReference("Transfers"),
transactionAmount = "+$20",
transactionAmountColor = themedColor { TangemTheme.colors.text.accent },
localTransactionText = null,
labelState = null,
notification = null,
buttons = persistentListOf(
ButtonState(
text = resourceReference(R.string.tangem_pay_get_help),
onClick = {},
),
),
dismiss = {},
),
TangemPayTxHistoryDetailsUM(
isBalanceHidden = false,
title = stringReference("12 June • 12:40"),
iconState = ImageReference.Res(R.drawable.ic_arrow_up_24),
transactionTitle = stringReference("Withdrawal"),
transactionSubtitle = stringReference("Transfers"),
transactionAmount = "-$5.86",
transactionAmountColor = themedColor { TangemTheme.colors.text.primary1 },
localTransactionText = null,
labelState = null,
notification = null,
buttons = persistentListOf(
ButtonState(
text = resourceReference(R.string.tangem_pay_get_help),
onClick = {},
),
),
dismiss = {},
),
),
)

View file

@ -60,29 +60,20 @@ internal object TangemPayMessagesFactory {
} }
} }
fun createFreezeCardMessage(isRedesignEnabled: Boolean, onFreezeClicked: () -> Unit): BottomSheetMessage { fun createFreezeCardMessage(onFreezeClicked: () -> Unit): BottomSheetMessage {
return bottomSheetMessage { return bottomSheetMessage {
infoBlock { infoBlock {
if (isRedesignEnabled) { vector(Icons.ic_snowflake_20) {
vector(Icons.ic_snowflake_20) { type = MessageBottomSheetUM.Vector.Type.Informative
type = MessageBottomSheetUM.Vector.Type.Informative backgroundType = MessageBottomSheetUM.Vector.BackgroundType.Informative
backgroundType = MessageBottomSheetUM.Vector.BackgroundType.Informative
}
} else {
icon(R.drawable.ic_snow_24) {
type = MessageBottomSheetUM.Icon.Type.Accent
backgroundType = MessageBottomSheetUM.Icon.BackgroundType.Accent
}
} }
title = TextReference.Res(R.string.tangem_pay_freeze_card_alert_title) title = TextReference.Res(R.string.tangem_pay_freeze_card_alert_title)
body = TextReference.Res(R.string.tangem_pay_freeze_card_alert_body) body = TextReference.Res(R.string.tangem_pay_freeze_card_alert_body)
} }
if (isRedesignEnabled) { secondaryButton {
secondaryButton { text = resourceReference(R.string.common_cancel)
text = resourceReference(R.string.common_cancel) onClick {
onClick { closeBs()
closeBs()
}
} }
} }
primaryButton { primaryButton {
@ -95,29 +86,20 @@ internal object TangemPayMessagesFactory {
} }
} }
fun createUnfreezeCardMessage(isRedesignEnabled: Boolean, onUnfreezeClicked: () -> Unit): BottomSheetMessage { fun createUnfreezeCardMessage(onUnfreezeClicked: () -> Unit): BottomSheetMessage {
return bottomSheetMessage { return bottomSheetMessage {
infoBlock { infoBlock {
if (isRedesignEnabled) { vector(Icons.ic_sun_20) {
vector(Icons.ic_sun_20) { type = MessageBottomSheetUM.Vector.Type.Attention
type = MessageBottomSheetUM.Vector.Type.Attention backgroundType = MessageBottomSheetUM.Vector.BackgroundType.Attention
backgroundType = MessageBottomSheetUM.Vector.BackgroundType.Attention
}
} else {
icon(R.drawable.ic_snow_24) {
type = MessageBottomSheetUM.Icon.Type.Accent
backgroundType = MessageBottomSheetUM.Icon.BackgroundType.Accent
}
} }
title = TextReference.Res(R.string.tangem_pay_unfreeze_card_alert_title) title = TextReference.Res(R.string.tangem_pay_unfreeze_card_alert_title)
body = TextReference.Res(R.string.tangem_pay_unfreeze_card_alert_body) body = TextReference.Res(R.string.tangem_pay_unfreeze_card_alert_body)
} }
if (isRedesignEnabled) { secondaryButton {
secondaryButton { text = resourceReference(R.string.common_cancel)
text = resourceReference(R.string.common_cancel) onClick {
onClick { closeBs()
closeBs()
}
} }
} }
primaryButton { primaryButton {

View file

@ -29,7 +29,6 @@ internal class TangemPayDetailsStateFactoryTest {
onBack = {}, onBack = {},
onOpenMenu = {}, onOpenMenu = {},
intents = intents, intents = intents,
isRedesignEnabled = true,
isRemoveAccountEnabled = true, isRemoveAccountEnabled = true,
isTiersPlusPlanEnabled = true, isTiersPlusPlanEnabled = true,
) )

View file

@ -13,7 +13,6 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
import com.tangem.domain.pay.usecase.SetTangemPayCardLimitUseCase import com.tangem.domain.pay.usecase.SetTangemPayCardLimitUseCase
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
@ -36,7 +35,6 @@ internal class TangemPayCardLimitSetupModelTest {
private val setLimitUseCase: SetTangemPayCardLimitUseCase = mockk(relaxed = true) private val setLimitUseCase: SetTangemPayCardLimitUseCase = mockk(relaxed = true)
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier = mockk() private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier = mockk()
private val analytics: AnalyticsEventHandler = mockk(relaxed = true) private val analytics: AnalyticsEventHandler = mockk(relaxed = true)
private val featureToggles: TangemPayFeatureToggles = mockk()
private val initialCard = TangemPayCard( private val initialCard = TangemPayCard(
id = cardId, id = cardId,
@ -98,7 +96,6 @@ internal class TangemPayCardLimitSetupModelTest {
setTangemPayCardLimitUseCase = setLimitUseCase, setTangemPayCardLimitUseCase = setLimitUseCase,
uiMessageSender = uiMessageSender, uiMessageSender = uiMessageSender,
analytics = analytics, analytics = analytics,
featureToggles = featureToggles,
) )
} }

View file

@ -14,7 +14,6 @@ import com.tangem.domain.models.pay.TangemPayCardState
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
import com.tangem.domain.pay.usecase.UpdateTangemPayCardNameUseCase import com.tangem.domain.pay.usecase.UpdateTangemPayCardNameUseCase
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayEditDisplayNameComponent import com.tangem.features.tangempay.components.TangemPayEditDisplayNameComponent
import com.tangem.features.tangempay.model.controller.TangemPayCardDetailsController import com.tangem.features.tangempay.model.controller.TangemPayCardDetailsController
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
@ -36,7 +35,6 @@ internal class TangemPayEditDisplayNameModelTest {
private val updateCardNameUseCase: UpdateTangemPayCardNameUseCase = mockk() private val updateCardNameUseCase: UpdateTangemPayCardNameUseCase = mockk()
private val uiMessageSender: UiMessageSender = mockk(relaxed = true) private val uiMessageSender: UiMessageSender = mockk(relaxed = true)
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier = mockk() private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier = mockk()
private val featureToggles: TangemPayFeatureToggles = mockk(relaxed = true)
private val cardDetailsControllerFactory: TangemPayCardDetailsController.Factory = mockk(relaxed = true) private val cardDetailsControllerFactory: TangemPayCardDetailsController.Factory = mockk(relaxed = true)
init { init {
@ -55,7 +53,6 @@ internal class TangemPayEditDisplayNameModelTest {
updateCardNameUseCase = updateCardNameUseCase, updateCardNameUseCase = updateCardNameUseCase,
uiMessageSender = uiMessageSender, uiMessageSender = uiMessageSender,
paymentAccountStatusSupplier = paymentAccountStatusSupplier, paymentAccountStatusSupplier = paymentAccountStatusSupplier,
featureToggles = featureToggles,
cardDetailsControllerFactory = cardDetailsControllerFactory, cardDetailsControllerFactory = cardDetailsControllerFactory,
) )

View file

@ -67,7 +67,7 @@ internal class TangemPayTxHistoryDetailsModelTest {
advanceUntilIdle() advanceUntilIdle()
// Assert // Assert
assertThat(model.uiState.value.redesign.detail).isEqualTo(TransactionDetailUM.Loading) assertThat(model.uiState.value.detail).isEqualTo(TransactionDetailUM.Loading)
model.onDestroy() model.onDestroy()
} }
@ -85,7 +85,7 @@ internal class TangemPayTxHistoryDetailsModelTest {
advanceUntilIdle() advanceUntilIdle()
// Assert // Assert
assertThat(model.uiState.value.redesign.detail) assertThat(model.uiState.value.detail)
.isEqualTo( .isEqualTo(
TransactionDetailUM.Content( TransactionDetailUM.Content(
cardNumber = stringReference("*4321"), cardNumber = stringReference("*4321"),
@ -106,7 +106,7 @@ internal class TangemPayTxHistoryDetailsModelTest {
advanceUntilIdle() advanceUntilIdle()
// Assert // Assert
assertThat(model.uiState.value.redesign.detail).isNull() assertThat(model.uiState.value.detail).isNull()
model.onDestroy() model.onDestroy()
} }
@ -120,7 +120,7 @@ internal class TangemPayTxHistoryDetailsModelTest {
advanceUntilIdle() advanceUntilIdle()
// Assert // Assert
assertThat(model.uiState.value.redesign.detail).isInstanceOf(TransactionDetailUM.Error::class.java) assertThat(model.uiState.value.detail).isInstanceOf(TransactionDetailUM.Error::class.java)
model.onDestroy() model.onDestroy()
} }
@ -136,7 +136,7 @@ internal class TangemPayTxHistoryDetailsModelTest {
) )
val model = createModel(testScope = this, transaction = spendTransaction()) val model = createModel(testScope = this, transaction = spendTransaction())
advanceUntilIdle() advanceUntilIdle()
val errorState = model.uiState.value.redesign.detail val errorState = model.uiState.value.detail
assertThat(errorState).isInstanceOf(TransactionDetailUM.Error::class.java) assertThat(errorState).isInstanceOf(TransactionDetailUM.Error::class.java)
// Act — tap refresh // Act — tap refresh
@ -144,7 +144,7 @@ internal class TangemPayTxHistoryDetailsModelTest {
advanceUntilIdle() advanceUntilIdle()
// Assert // Assert
assertThat(model.uiState.value.redesign.detail) assertThat(model.uiState.value.detail)
.isEqualTo( .isEqualTo(
TransactionDetailUM.Content( TransactionDetailUM.Content(
cardNumber = stringReference("*4321"), cardNumber = stringReference("*4321"),
@ -164,7 +164,7 @@ internal class TangemPayTxHistoryDetailsModelTest {
advanceUntilIdle() advanceUntilIdle()
// Assert // Assert
assertThat(model.uiState.value.redesign.detail).isNull() assertThat(model.uiState.value.detail).isNull()
coVerify(exactly = 0) { repository.getTransaction(any(), any()) } coVerify(exactly = 0) { repository.getTransaction(any(), any()) }
model.onDestroy() model.onDestroy()
} }