Updated on 2026-08-14
This commit is contained in:
parent
ebe84a667e
commit
eb2c1d765a
31 changed files with 1034 additions and 123 deletions
|
|
@ -14,6 +14,7 @@ import com.tangem.core.decompose.context.childByContext
|
|||
import com.tangem.core.decompose.navigation.inner.InnerRouter
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.tangempay.navigation.TangemPayDetailsInnerRoute
|
||||
import com.tangem.features.tokenreceive.TokenReceiveComponent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -21,6 +22,7 @@ import dagger.assisted.AssistedInject
|
|||
internal class DefaultTangemPayCardPageComponent @AssistedInject constructor(
|
||||
@Assisted private val appComponentContext: AppComponentContext,
|
||||
@Assisted private val params: TangemPayCardPageComponent.Params,
|
||||
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
|
||||
) : AppComponentContext by appComponentContext, TangemPayCardPageComponent {
|
||||
|
||||
private val stackNavigation = StackNavigation<TangemPayDetailsInnerRoute>()
|
||||
|
|
@ -56,6 +58,7 @@ internal class DefaultTangemPayCardPageComponent @AssistedInject constructor(
|
|||
TangemPayDetailsInnerRoute.Details -> TangemPayCardPageScreenComponent(
|
||||
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
|
||||
params = params,
|
||||
tokenReceiveComponentFactory = tokenReceiveComponentFactory,
|
||||
)
|
||||
TangemPayDetailsInnerRoute.ChangePIN -> TangemPayChangePinComponent(
|
||||
appComponentContext = childByContext(componentContext = componentContext, router = innerRouter),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
|
|
@ -19,10 +20,12 @@ import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetails
|
|||
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
|
||||
import com.tangem.features.tangempay.model.TangemPayCardPageModel
|
||||
import com.tangem.features.tangempay.ui.TangemPayCardPageScreen
|
||||
import com.tangem.features.tokenreceive.TokenReceiveComponent
|
||||
|
||||
internal class TangemPayCardPageScreenComponent(
|
||||
private val appComponentContext: AppComponentContext,
|
||||
private val params: TangemPayCardPageComponent.Params,
|
||||
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
|
||||
) : AppComponentContext by appComponentContext, ComposableContentComponent {
|
||||
|
||||
private val model: TangemPayCardPageModel = getOrCreateModel(params = params)
|
||||
|
|
@ -57,7 +60,9 @@ internal class TangemPayCardPageScreenComponent(
|
|||
TangemPayCardPageScreen(
|
||||
state = state,
|
||||
cardDetailsBlockComponent = cardDetailsBlockComponent,
|
||||
cardDetailsState = cardDetailsState,
|
||||
cardDetailsState = cardDetailsState.copy(
|
||||
isActive = !state.isReissueInProgress,
|
||||
),
|
||||
modifier = modifier,
|
||||
)
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
|
|
@ -77,6 +82,32 @@ internal class TangemPayCardPageScreenComponent(
|
|||
listener = model,
|
||||
),
|
||||
)
|
||||
is TangemPayDetailsNavigation.ReissueCard -> TangemPayReissueCardComponent(
|
||||
appComponentContext = context,
|
||||
params = TangemPayReissueCardComponent.Params(
|
||||
listener = model,
|
||||
userWalletId = params.userWalletId,
|
||||
cardId = params.config.cardId,
|
||||
),
|
||||
)
|
||||
is TangemPayDetailsNavigation.AddFunds -> TangemPayAddFundsComponent(
|
||||
appComponentContext = context,
|
||||
params = TangemPayAddFundsComponent.Params(
|
||||
listener = model,
|
||||
walletId = navigation.walletId,
|
||||
cryptoBalance = navigation.cryptoBalance,
|
||||
fiatBalance = navigation.fiatBalance,
|
||||
depositAddress = navigation.depositAddress,
|
||||
chainId = navigation.chainId,
|
||||
),
|
||||
)
|
||||
is TangemPayDetailsNavigation.Receive -> tokenReceiveComponentFactory.create(
|
||||
context = context,
|
||||
params = TokenReceiveComponent.Params(
|
||||
config = navigation.config,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
else -> error("Unsupported bottom sheet navigation: $navigation")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ internal class TangemPayDetailsComponent(
|
|||
listener = model,
|
||||
),
|
||||
)
|
||||
else -> error("Unsupported bottom sheet navigation: $navigation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.features.tangempay.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.model.TangemPayReissueOrderInfo
|
||||
import com.tangem.features.tangempay.model.TangemPayReissueCardModel
|
||||
import com.tangem.features.tangempay.ui.TangemPayReissueCardContent
|
||||
|
||||
internal class TangemPayReissueCardComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: Params,
|
||||
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: TangemPayReissueCardModel = getOrCreateModel(params = params)
|
||||
|
||||
override fun dismiss() = model.onDismiss()
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
TangemPayReissueCardContent(state = state)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val listener: ReissueCardListener,
|
||||
val userWalletId: UserWalletId,
|
||||
val cardId: String,
|
||||
)
|
||||
}
|
||||
|
||||
internal interface ReissueCardListener {
|
||||
fun onReissueOrderCreate(order: TangemPayReissueOrderInfo)
|
||||
fun onDismissReissueCard()
|
||||
fun onClickAddFunds()
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import com.tangem.features.tangempay.model.TangemPayDetailsModel
|
|||
import com.tangem.features.tangempay.model.TangemPayEditDisplayNameModel
|
||||
import com.tangem.features.tangempay.model.TangemPayTxHistoryDetailsModel
|
||||
import com.tangem.features.tangempay.model.TangemPayTxHistoryModel
|
||||
import com.tangem.features.tangempay.model.TangemPayReissueCardModel
|
||||
import com.tangem.features.tangempay.model.TangemPayViewPinModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -71,4 +72,9 @@ internal interface TangemPayModelModule {
|
|||
@IntoMap
|
||||
@ClassKey(TangemPayEditDisplayNameModel::class)
|
||||
fun bindTangemPayEditDisplayNameModel(model: TangemPayEditDisplayNameModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(TangemPayReissueCardModel::class)
|
||||
fun bindTangemPayReissueCardModel(model: TangemPayReissueCardModel): Model
|
||||
}
|
||||
|
|
@ -1,39 +1,37 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Immutable
|
||||
internal data class TangemPayCardPageUM(
|
||||
val addToWalletBlockState: AddToWalletBlockState? = null,
|
||||
val settings: ImmutableList<TangemPayCardPageSetting> = persistentListOf(
|
||||
TangemPayCardPageSetting.ChangePIN,
|
||||
TangemPayCardPageSetting.FreezeCard,
|
||||
),
|
||||
val settings: ImmutableList<TangemPayCardPageSetting>,
|
||||
val onBackClick: () -> Unit,
|
||||
val onSettingClick: (TangemPayCardPageSetting) -> Unit,
|
||||
val addToWalletBlockState: AddToWalletBlockState? = null,
|
||||
val isReissueInProgress: Boolean = false,
|
||||
) {
|
||||
companion object {
|
||||
fun stub(
|
||||
addToWalletBlockState: AddToWalletBlockState? = AddToWalletBlockState(onClick = {}, onClickClose = {}),
|
||||
settings: ImmutableList<TangemPayCardPageSetting> = persistentListOf(
|
||||
TangemPayCardPageSetting.ChangePIN,
|
||||
TangemPayCardPageSetting.FreezeCard,
|
||||
TangemPayCardPageSetting.ReplaceCard,
|
||||
TangemPayCardPageSetting(TextReference.Str("Pin Code")) {},
|
||||
TangemPayCardPageSetting(TextReference.Str("Freeze Card")) {},
|
||||
TangemPayCardPageSetting(TextReference.Str("Reissue Card")) {},
|
||||
),
|
||||
isReissueInProgress: Boolean = false,
|
||||
) = TangemPayCardPageUM(
|
||||
addToWalletBlockState = addToWalletBlockState,
|
||||
settings = settings,
|
||||
onBackClick = {},
|
||||
onSettingClick = {},
|
||||
isReissueInProgress = isReissueInProgress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
internal sealed class TangemPayCardPageSetting {
|
||||
data object ChangePIN : TangemPayCardPageSetting()
|
||||
data object FreezeCard : TangemPayCardPageSetting()
|
||||
data object ReplaceCard : TangemPayCardPageSetting()
|
||||
}
|
||||
internal data class TangemPayCardPageSetting(
|
||||
val title: TextReference,
|
||||
val onSettingClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -32,4 +32,7 @@ internal sealed class TangemPayDetailsNavigation {
|
|||
val userWalletId: UserWalletId,
|
||||
val cardId: String,
|
||||
) : TangemPayDetailsNavigation()
|
||||
|
||||
@Serializable
|
||||
data object ReissueCard : TangemPayDetailsNavigation()
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@ internal data class TangemPayCardDetailsUM(
|
|||
val isLoading: Boolean = false,
|
||||
val cardFrozenState: TangemPayCardFrozenState,
|
||||
val displayNameState: DisplayNameState?,
|
||||
val isActive: Boolean = true,
|
||||
)
|
||||
|
||||
internal sealed interface DisplayNameState {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
@Immutable
|
||||
internal data class TangemPayReissueCardUM(
|
||||
val feeAmount: String,
|
||||
val isFeeLoading: Boolean,
|
||||
val isReissuingInProgress: Boolean,
|
||||
val error: TangemPayReissueCardError?,
|
||||
val onConfirmClick: () -> Unit,
|
||||
val onRetryFee: () -> Unit,
|
||||
val onAddFundsClick: () -> Unit,
|
||||
val onDismissRequest: () -> Unit,
|
||||
) {
|
||||
companion object {
|
||||
fun stub(
|
||||
feeAmount: String = "$4.25",
|
||||
isFeeLoading: Boolean = false,
|
||||
error: TangemPayReissueCardError = TangemPayReissueCardError.InitialDataLoading,
|
||||
isReissuingInProgress: Boolean = false,
|
||||
) = TangemPayReissueCardUM(
|
||||
feeAmount = feeAmount,
|
||||
isFeeLoading = isFeeLoading,
|
||||
error = error,
|
||||
isReissuingInProgress = isReissuingInProgress,
|
||||
onConfirmClick = {},
|
||||
onRetryFee = {},
|
||||
onAddFundsClick = {},
|
||||
onDismissRequest = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal enum class TangemPayReissueCardError {
|
||||
InsufficientFunds, InitialDataLoading
|
||||
}
|
||||
|
|
@ -4,15 +4,27 @@ import androidx.compose.runtime.Stable
|
|||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.pay.model.OrderStatus
|
||||
import com.tangem.domain.pay.model.TangemPayReissueOrderInfo
|
||||
import com.tangem.domain.pay.model.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayReissueCardRepository
|
||||
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import com.tangem.features.tangempay.components.AddFundsListener
|
||||
import com.tangem.features.tangempay.components.ReissueCardListener
|
||||
import com.tangem.features.tangempay.components.TangemPayCardPageComponent
|
||||
import com.tangem.features.tangempay.components.ViewPinListener
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
|
|
@ -25,6 +37,7 @@ import com.tangem.features.tangempay.utils.TangemPayMessagesFactory
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
|
@ -33,43 +46,55 @@ import kotlinx.coroutines.flow.update
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class TangemPayCardPageModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val analytics: AnalyticsEventHandler,
|
||||
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : Model(), ViewPinListener {
|
||||
private val reissueCardRepository: TangemPayReissueCardRepository,
|
||||
) : Model(), ViewPinListener, ReissueCardListener, AddFundsListener {
|
||||
|
||||
private val params: TangemPayCardPageComponent.Params = paramsContainer.require()
|
||||
|
||||
private var currentFrozenState: TangemPayCardFrozenState = params.config.cardFrozenState
|
||||
|
||||
private val addToWalletBannerJobHolder = JobHolder()
|
||||
private val addFundsJobHolder = JobHolder()
|
||||
|
||||
val uiState: StateFlow<TangemPayCardPageUM>
|
||||
field = MutableStateFlow(
|
||||
TangemPayCardPageUM(
|
||||
onBackClick = router::pop,
|
||||
onSettingClick = ::onSettingClick,
|
||||
settings = persistentListOf(
|
||||
TangemPayCardPageSetting(
|
||||
title = TextReference.Res(R.string.tangempay_card_details_change_pin),
|
||||
onSettingClick = ::onClickChangePIN,
|
||||
),
|
||||
TangemPayCardPageSetting(
|
||||
title = TextReference.Res(R.string.tangempay_card_details_freeze_card),
|
||||
onSettingClick = ::onClickFreezeOrUnfreezeCard,
|
||||
),
|
||||
TangemPayCardPageSetting(
|
||||
title = TextReference.Res(R.string.tangempay_card_details_reissue_card),
|
||||
onSettingClick = ::onClickReissueCard,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<TangemPayDetailsNavigation> = SlotNavigation()
|
||||
|
||||
init {
|
||||
// TODO v_rodionov: #[REDACTED_TASK_KEY] check reissue order state before card details are showed
|
||||
fetchAddToWalletBanner()
|
||||
subscribeToCardFrozenState()
|
||||
}
|
||||
|
||||
private fun onSettingClick(setting: TangemPayCardPageSetting) = when (setting) {
|
||||
TangemPayCardPageSetting.ChangePIN -> onClickChangePIN()
|
||||
TangemPayCardPageSetting.FreezeCard -> onClickFreezeOrUnfreezeCard()
|
||||
TangemPayCardPageSetting.ReplaceCard -> Unit // TODO v_rodionov #[REDACTED_TASK_KEY]
|
||||
}
|
||||
|
||||
private fun onClickChangePIN() {
|
||||
if (!params.config.isPinSet) {
|
||||
router.push(TangemPayDetailsInnerRoute.ChangePIN)
|
||||
|
|
@ -94,6 +119,82 @@ internal class TangemPayCardPageModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun onClickReissueCard() {
|
||||
analytics.send(TangemPayAnalyticsEvents.ReplaceCardClicked())
|
||||
bottomSheetNavigation.activate(TangemPayDetailsNavigation.ReissueCard)
|
||||
}
|
||||
|
||||
override fun onReissueOrderCreate(order: TangemPayReissueOrderInfo) {
|
||||
bottomSheetNavigation.dismiss()
|
||||
onReissueOrderStatusReceived(order.orderStatus)
|
||||
if (order.orderStatus != OrderStatus.CANCELED) {
|
||||
modelScope.launch {
|
||||
reissueCardRepository.storeReissueOrderId(params.config.cardId, order.orderId)
|
||||
}
|
||||
} else {
|
||||
uiMessageSender.send(SnackbarMessage(resourceReference(R.string.common_something_went_wrong)))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDismissReissueCard() {
|
||||
bottomSheetNavigation.dismiss()
|
||||
}
|
||||
|
||||
override fun onClickAddFunds() {
|
||||
bottomSheetNavigation.dismiss()
|
||||
modelScope.launch {
|
||||
val balance = cardDetailsRepository.getCardBalance(params.userWalletId).getOrNull()
|
||||
val depositAddress = balance?.depositAddress
|
||||
if (balance == null || depositAddress == null) {
|
||||
uiMessageSender.send(SnackbarMessage(resourceReference(R.string.common_error)))
|
||||
return@launch
|
||||
}
|
||||
bottomSheetNavigation.activate(
|
||||
TangemPayDetailsNavigation.AddFunds(
|
||||
walletId = params.userWalletId,
|
||||
fiatBalance = balance.fiatBalance,
|
||||
cryptoBalance = balance.cryptoBalance,
|
||||
depositAddress = depositAddress,
|
||||
chainId = params.config.chainId,
|
||||
),
|
||||
)
|
||||
}.saveIn(addFundsJobHolder)
|
||||
}
|
||||
|
||||
override fun onClickReceive(data: TangemPayTopUpData) {
|
||||
bottomSheetNavigation.dismiss()
|
||||
val config = TokenReceiveConfig(
|
||||
shouldShowWarning = true,
|
||||
cryptoCurrency = data.currency,
|
||||
userWalletId = data.walletId,
|
||||
showMemoDisclaimer = false,
|
||||
receiveAddress = data.receiveAddress,
|
||||
)
|
||||
bottomSheetNavigation.activate(TangemPayDetailsNavigation.Receive(config))
|
||||
}
|
||||
|
||||
override fun onClickSwap(data: TangemPayTopUpData) {
|
||||
bottomSheetNavigation.dismiss()
|
||||
router.push(
|
||||
AppRoute.Swap(
|
||||
currencyFrom = data.currency,
|
||||
userWalletId = data.walletId,
|
||||
isInitialReverseOrder = true,
|
||||
screenSource = AnalyticsParam.ScreensSources.TangemPay.value,
|
||||
tangemPayInput = AppRoute.Swap.TangemPayInput(
|
||||
cryptoAmount = data.cryptoBalance,
|
||||
fiatAmount = data.fiatBalance,
|
||||
depositAddress = data.depositAddress,
|
||||
isWithdrawal = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDismissAddFunds() {
|
||||
bottomSheetNavigation.dismiss()
|
||||
}
|
||||
|
||||
private fun freezeCard() {
|
||||
modelScope.launch {
|
||||
cardDetailsRepository.freezeCard(
|
||||
|
|
@ -174,4 +275,18 @@ internal class TangemPayCardPageModel @Inject constructor(
|
|||
override fun onDismissViewPin() {
|
||||
bottomSheetNavigation.dismiss()
|
||||
}
|
||||
|
||||
private fun onReissueOrderStatusReceived(orderStatus: OrderStatus) {
|
||||
when (orderStatus) {
|
||||
OrderStatus.NEW, OrderStatus.PROCESSING, OrderStatus.COMPLETED, OrderStatus.UNKNOWN -> {
|
||||
uiState.update { state ->
|
||||
state.copy(
|
||||
addToWalletBlockState = null,
|
||||
isReissueInProgress = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
OrderStatus.CANCELED -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package com.tangem.features.tangempay.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.getJavaCurrencyByCode
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayReissueCardRepository
|
||||
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
||||
import com.tangem.features.tangempay.components.TangemPayReissueCardComponent
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayReissueCardError
|
||||
import com.tangem.features.tangempay.entity.TangemPayReissueCardUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class TangemPayReissueCardModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||
private val reissueCardRepository: TangemPayReissueCardRepository,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
private val analytics: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<TangemPayReissueCardComponent.Params>()
|
||||
private val reissueJobHolder = JobHolder()
|
||||
private val loadDataJobHolder = JobHolder()
|
||||
|
||||
val state: StateFlow<TangemPayReissueCardUM>
|
||||
field = MutableStateFlow(
|
||||
TangemPayReissueCardUM(
|
||||
feeAmount = "",
|
||||
isFeeLoading = true,
|
||||
error = null,
|
||||
isReissuingInProgress = false,
|
||||
onConfirmClick = ::onConfirm,
|
||||
onRetryFee = ::loadData,
|
||||
onAddFundsClick = { params.listener.onClickAddFunds() },
|
||||
onDismissRequest = ::onDismiss,
|
||||
),
|
||||
)
|
||||
|
||||
init {
|
||||
analytics.send(TangemPayAnalyticsEvents.ReplaceCardConfirmationPopupOpened())
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onDismiss() {
|
||||
reissueJobHolder.cancel()
|
||||
params.listener.onDismissReissueCard()
|
||||
}
|
||||
|
||||
private fun onConfirm() {
|
||||
analytics.send(TangemPayAnalyticsEvents.ReplaceCardConfirmed())
|
||||
state.update { it.copy(isReissuingInProgress = true) }
|
||||
modelScope.launch {
|
||||
reissueCardRepository.reissueCard(
|
||||
userWalletId = params.userWalletId,
|
||||
cardId = params.cardId,
|
||||
).onLeft {
|
||||
uiMessageSender.send(SnackbarMessage(resourceReference(R.string.common_something_went_wrong)))
|
||||
onDismiss()
|
||||
}.onRight { order ->
|
||||
params.listener.onReissueOrderCreate(order)
|
||||
}
|
||||
}.saveIn(reissueJobHolder)
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
state.update { it.copy(isFeeLoading = true, error = null) }
|
||||
|
||||
modelScope.launch {
|
||||
val (cardBalance, fee) = coroutineScope {
|
||||
val balanceDeferred = async { cardDetailsRepository.getCardBalance(params.userWalletId).getOrNull() }
|
||||
val feeDeferred = async { reissueCardRepository.getReissueCardFee(params.userWalletId).getOrNull() }
|
||||
balanceDeferred.await() to feeDeferred.await()
|
||||
}
|
||||
|
||||
val error = if (fee == null || cardBalance == null) {
|
||||
TangemPayReissueCardError.InitialDataLoading
|
||||
} else if (cardBalance.availableForWithdrawal < fee.amount) {
|
||||
TangemPayReissueCardError.InsufficientFunds
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
state.update { state ->
|
||||
state.copy(
|
||||
feeAmount = fee?.let {
|
||||
fee.amount.format {
|
||||
val symbol = getJavaCurrencyByCode(fee.currencyCode).symbol
|
||||
fiat(fee.currencyCode, symbol)
|
||||
}
|
||||
}.orEmpty(),
|
||||
isFeeLoading = false,
|
||||
error = error,
|
||||
)
|
||||
}
|
||||
}.saveIn(loadDataJobHolder)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,12 +12,27 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
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.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -27,8 +42,6 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
|
|
@ -45,6 +58,8 @@ 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.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButton
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
|
|
@ -53,12 +68,12 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.models.account.CardDisplayName
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.DisplayNameState
|
||||
import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM
|
||||
import com.tangem.features.tangempay.model.CardDataType
|
||||
import com.tangem.domain.models.account.CardDisplayName
|
||||
|
||||
private const val ICON_FADE_DURATION_MS = 300
|
||||
private val CustomCardBlockColor = Color(0x1F828282)
|
||||
|
|
@ -101,7 +116,7 @@ internal fun TangemPayCard(state: TangemPayCardDetailsUM, modifier: Modifier = M
|
|||
shape = RoundedCornerShape(16.dp),
|
||||
),
|
||||
) {
|
||||
if (shouldShowDetails) {
|
||||
if (shouldShowDetails && state.isActive) {
|
||||
TangemPayCardDetailsShownBlock(
|
||||
cardNumber = state.number,
|
||||
expiry = state.expiry,
|
||||
|
|
@ -114,11 +129,7 @@ internal fun TangemPayCard(state: TangemPayCardDetailsUM, modifier: Modifier = M
|
|||
)
|
||||
} else {
|
||||
TangemPayCardDetailsHiddenBlock(
|
||||
cardFrozenState = state.cardFrozenState,
|
||||
isLoading = state.isLoading,
|
||||
shortCardNumber = state.numberShort,
|
||||
onShowDetails = state.onClick,
|
||||
displayNameState = state.displayNameState,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -126,16 +137,9 @@ internal fun TangemPayCard(state: TangemPayCardDetailsUM, modifier: Modifier = M
|
|||
|
||||
@Suppress("LongMethod", "DestructuringDeclarationWithTooManyEntries")
|
||||
@Composable
|
||||
private fun TangemPayCardDetailsHiddenBlock(
|
||||
shortCardNumber: String,
|
||||
cardFrozenState: TangemPayCardFrozenState,
|
||||
isLoading: Boolean,
|
||||
onShowDetails: () -> Unit,
|
||||
displayNameState: DisplayNameState?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
private fun TangemPayCardDetailsHiddenBlock(state: TangemPayCardDetailsUM, modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
val imageResId = when (cardFrozenState) {
|
||||
val imageResId = when (state.cardFrozenState) {
|
||||
is TangemPayCardFrozenState.Frozen -> R.drawable.img_tangem_pay_visa_frozen
|
||||
else -> R.drawable.img_tangem_pay_visa
|
||||
}
|
||||
|
|
@ -144,75 +148,78 @@ private fun TangemPayCardDetailsHiddenBlock(
|
|||
painter = painterResource(id = imageResId),
|
||||
contentDescription = null,
|
||||
)
|
||||
ConstraintLayout(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
val (displayNameRef, cardNumberRef, frozenIconRef, buttonRef) = createRefs()
|
||||
|
||||
if (displayNameState != null) {
|
||||
CardDisplayName(
|
||||
state = displayNameState,
|
||||
modifier = Modifier.constrainAs(displayNameRef) {
|
||||
start.linkTo(parent.start)
|
||||
bottom.linkTo(cardNumberRef.top)
|
||||
width = Dimension.wrapContent
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = shortCardNumber,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
if (state.isActive) {
|
||||
ConstraintLayout(
|
||||
modifier = Modifier
|
||||
.constrainAs(cardNumberRef) {
|
||||
start.linkTo(parent.start)
|
||||
bottom.linkTo(parent.bottom)
|
||||
}
|
||||
.padding(bottom = 8.dp),
|
||||
)
|
||||
when (cardFrozenState) {
|
||||
is TangemPayCardFrozenState.Frozen -> 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),
|
||||
painter = painterResource(id = R.drawable.ic_snow_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.constant,
|
||||
)
|
||||
TangemPayCardFrozenState.Pending -> CircularProgressIndicator(
|
||||
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),
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
strokeWidth = 1.dp,
|
||||
)
|
||||
TangemPayCardFrozenState.Unfrozen -> Unit
|
||||
}
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
val (displayNameRef, cardNumberRef, frozenIconRef, buttonRef) = createRefs()
|
||||
|
||||
TangemPayCardDetailsCustomButton(
|
||||
modifier = Modifier.constrainAs(buttonRef) {
|
||||
end.linkTo(parent.end)
|
||||
bottom.linkTo(parent.bottom)
|
||||
},
|
||||
text = stringResourceSafe(id = R.string.tangempay_card_details_show_details),
|
||||
onClick = onShowDetails,
|
||||
showProgress = isLoading,
|
||||
)
|
||||
if (state.displayNameState != null) {
|
||||
CardDisplayName(
|
||||
state = state.displayNameState,
|
||||
modifier = Modifier.constrainAs(displayNameRef) {
|
||||
start.linkTo(parent.start)
|
||||
bottom.linkTo(cardNumberRef.top)
|
||||
width = Dimension.wrapContent
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = state.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),
|
||||
)
|
||||
when (state.cardFrozenState) {
|
||||
is TangemPayCardFrozenState.Frozen -> 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),
|
||||
painter = painterResource(id = R.drawable.ic_snow_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.constant,
|
||||
)
|
||||
TangemPayCardFrozenState.Pending -> CircularProgressIndicator(
|
||||
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),
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
strokeWidth = 1.dp,
|
||||
)
|
||||
TangemPayCardFrozenState.Unfrozen -> Unit
|
||||
}
|
||||
|
||||
TangemPayCardDetailsCustomButton(
|
||||
modifier = Modifier.constrainAs(buttonRef) {
|
||||
end.linkTo(parent.end)
|
||||
bottom.linkTo(parent.bottom)
|
||||
},
|
||||
text = stringResourceSafe(id = R.string.tangempay_card_details_show_details),
|
||||
onClick = state.onClick,
|
||||
showProgress = state.isLoading,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ import androidx.compose.ui.platform.LocalDensity
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
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
|
||||
|
|
@ -46,12 +50,6 @@ import com.tangem.features.tangempay.entity.TangemPayCardPageUM
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
private const val CONTENT_FADE_DURATION_MS = 300
|
||||
private val TangemPayCardPageSetting.titleRes
|
||||
get() = when (this) {
|
||||
TangemPayCardPageSetting.ChangePIN -> R.string.tangempay_card_details_change_pin
|
||||
TangemPayCardPageSetting.FreezeCard -> R.string.tangempay_card_details_freeze_card
|
||||
TangemPayCardPageSetting.ReplaceCard -> R.string.common_error // TODO v_rodionov #[REDACTED_TASK_KEY]
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayCardPageScreen(
|
||||
|
|
@ -110,20 +108,35 @@ internal fun TangemPayCardPageScreen(
|
|||
enter = fadeIn(animationSpec = tween(CONTENT_FADE_DURATION_MS)),
|
||||
exit = fadeOut(animationSpec = tween(CONTENT_FADE_DURATION_MS)),
|
||||
) {
|
||||
TangemPayCardPageSettingsBlock(
|
||||
settings = state.settings,
|
||||
onSettingClick = state.onSettingClick,
|
||||
)
|
||||
if (state.isReissueInProgress) {
|
||||
TangemPayReplacingCardBlock()
|
||||
} else {
|
||||
TangemPayCardPageSettingsBlock(
|
||||
settings = state.settings,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemPayReplacingCardBlock(modifier: Modifier = Modifier) {
|
||||
Notification(
|
||||
modifier = modifier,
|
||||
config = NotificationConfig(
|
||||
iconResId = com.tangem.core.ui.R.drawable.ic_update_32,
|
||||
iconTint = NotificationConfig.IconTint.Accent,
|
||||
title = resourceReference(R.string.tangempay_reissue_card_in_progress),
|
||||
subtitle = resourceReference(R.string.tangempay_reissue_card_in_progress_description),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemPayCardPageSettingsBlock(
|
||||
settings: ImmutableList<TangemPayCardPageSetting>,
|
||||
onSettingClick: (TangemPayCardPageSetting) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -147,7 +160,7 @@ private fun TangemPayCardPageSettingsBlock(
|
|||
settings.fastForEach { item ->
|
||||
TangemPayCardPageSettingRow(
|
||||
item = item,
|
||||
onClick = { onSettingClick(item) },
|
||||
onClick = item.onSettingClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -167,7 +180,7 @@ private fun TangemPayCardPageSettingRow(
|
|||
contentAlignment = Alignment.CenterStart,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(item.titleRes),
|
||||
text = item.title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,218 @@
|
|||
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(),
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue