Updated on 2026-08-14
This commit is contained in:
parent
e037233aa5
commit
804e4eca23
31 changed files with 635 additions and 147 deletions
|
|
@ -4,49 +4,80 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
|||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig.ShowRefreshState
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.themedColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class TangemPayDetailsStateFactory(
|
||||
private val onBack: () -> Unit,
|
||||
private val onRefresh: (ShowRefreshState) -> Unit,
|
||||
private val onAddFunds: () -> Unit,
|
||||
private val onClickChangePin: () -> Unit,
|
||||
private val isCardFrozen: Boolean,
|
||||
private val onClickFreezeCard: () -> Unit,
|
||||
private val onClickUnfreezeCard: () -> Unit,
|
||||
) {
|
||||
|
||||
fun getInitialState() = TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(
|
||||
onBackClick = onBack,
|
||||
items = persistentListOf(
|
||||
TangemDropdownMenuItem(
|
||||
title = TextReference.Res(R.string.tangempay_card_details_change_pin),
|
||||
fun getInitialState(): TangemPayDetailsUM {
|
||||
val cardFrozenStateItem = if (isCardFrozen) {
|
||||
TangemPayDetailsTopBarMenuItem(
|
||||
type = TangemPayDetailsTopBarMenuItemType.UnfreezeCard,
|
||||
dropdownItem = TangemDropdownMenuItem(
|
||||
title = resourceReference(R.string.tangempay_card_details_unfreeze_card),
|
||||
textColor = themedColor { TangemTheme.colors.text.primary1 },
|
||||
onClick = onClickChangePin,
|
||||
onClick = onClickUnfreezeCard,
|
||||
),
|
||||
TangemDropdownMenuItem(
|
||||
title = TextReference.Res(R.string.tangempay_card_details_freeze_card),
|
||||
)
|
||||
} else {
|
||||
TangemPayDetailsTopBarMenuItem(
|
||||
type = TangemPayDetailsTopBarMenuItemType.FreezeCard,
|
||||
dropdownItem = TangemDropdownMenuItem(
|
||||
title = resourceReference(R.string.tangempay_card_details_freeze_card),
|
||||
textColor = themedColor { TangemTheme.colors.text.primary1 },
|
||||
onClick = onClickFreezeCard,
|
||||
),
|
||||
),
|
||||
),
|
||||
pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = onRefresh),
|
||||
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(
|
||||
actionButtons = persistentListOf(
|
||||
ActionButtonConfig(
|
||||
text = resourceReference(id = R.string.tangempay_card_details_add_funds),
|
||||
iconResId = R.drawable.ic_arrow_down_24,
|
||||
onClick = onAddFunds,
|
||||
)
|
||||
}
|
||||
|
||||
return TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(
|
||||
onBackClick = onBack,
|
||||
items = persistentListOf(
|
||||
TangemPayDetailsTopBarMenuItem(
|
||||
type = TangemPayDetailsTopBarMenuItemType.ChangePin,
|
||||
dropdownItem = TangemDropdownMenuItem(
|
||||
title = resourceReference(R.string.tangempay_card_details_change_pin),
|
||||
textColor = themedColor { TangemTheme.colors.text.primary1 },
|
||||
onClick = onClickChangePin,
|
||||
),
|
||||
),
|
||||
cardFrozenStateItem,
|
||||
),
|
||||
),
|
||||
),
|
||||
addToWalletBlockState = null,
|
||||
isBalanceHidden = false,
|
||||
addFundsEnabled = true,
|
||||
)
|
||||
pullToRefreshConfig = PullToRefreshConfig(
|
||||
isRefreshing = false,
|
||||
onRefresh = onRefresh,
|
||||
),
|
||||
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(
|
||||
actionButtons = persistentListOf(
|
||||
ActionButtonConfig(
|
||||
text = resourceReference(id = R.string.tangempay_card_details_add_funds),
|
||||
iconResId = R.drawable.ic_arrow_down_24,
|
||||
onClick = onAddFunds,
|
||||
),
|
||||
),
|
||||
frozenState = if (isCardFrozen) {
|
||||
CardFrozenState.Frozen(onClickUnfreezeCard)
|
||||
} else {
|
||||
CardFrozenState.Unfrozen
|
||||
},
|
||||
),
|
||||
addToWalletBlockState = null,
|
||||
isBalanceHidden = false,
|
||||
addFundsEnabled = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,5 +5,16 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
|
||||
internal data class TangemPayDetailsTopBarConfig(
|
||||
val onBackClick: () -> Unit,
|
||||
val items: ImmutableList<TangemDropdownMenuItem>?,
|
||||
)
|
||||
val items: ImmutableList<TangemPayDetailsTopBarMenuItem>?,
|
||||
)
|
||||
|
||||
internal data class TangemPayDetailsTopBarMenuItem(
|
||||
val type: TangemPayDetailsTopBarMenuItemType,
|
||||
val dropdownItem: TangemDropdownMenuItem,
|
||||
)
|
||||
|
||||
internal enum class TangemPayDetailsTopBarMenuItemType {
|
||||
ChangePin,
|
||||
FreezeCard,
|
||||
UnfreezeCard,
|
||||
}
|
||||
|
|
@ -15,9 +15,9 @@ internal data class TangemPayDetailsUM(
|
|||
)
|
||||
|
||||
internal data class TangemPayCardDetailsUM(
|
||||
val number: String = "",
|
||||
val expiry: String = "",
|
||||
val cvv: String = "",
|
||||
val number: String,
|
||||
val expiry: String,
|
||||
val cvv: String,
|
||||
val buttonText: TextReference = TextReference.EMPTY,
|
||||
val onClick: () -> Unit = {},
|
||||
val onCopy: (String) -> Unit = {},
|
||||
|
|
@ -28,13 +28,16 @@ internal data class TangemPayCardDetailsUM(
|
|||
internal sealed class TangemPayDetailsBalanceBlockState {
|
||||
|
||||
abstract val actionButtons: ImmutableList<ActionButtonConfig>
|
||||
abstract val frozenState: CardFrozenState
|
||||
|
||||
data class Loading(
|
||||
override val actionButtons: ImmutableList<ActionButtonConfig>,
|
||||
override val frozenState: CardFrozenState,
|
||||
) : TangemPayDetailsBalanceBlockState()
|
||||
|
||||
data class Content(
|
||||
override val actionButtons: ImmutableList<ActionButtonConfig>,
|
||||
override val frozenState: CardFrozenState,
|
||||
val cryptoBalance: String,
|
||||
val fiatBalance: String,
|
||||
val isBalanceFlickering: Boolean,
|
||||
|
|
@ -42,9 +45,16 @@ internal sealed class TangemPayDetailsBalanceBlockState {
|
|||
|
||||
data class Error(
|
||||
override val actionButtons: ImmutableList<ActionButtonConfig>,
|
||||
override val frozenState: CardFrozenState,
|
||||
) : TangemPayDetailsBalanceBlockState()
|
||||
}
|
||||
|
||||
sealed class CardFrozenState {
|
||||
data object Pending : CardFrozenState()
|
||||
data class Frozen(val onUnfreeze: () -> Unit) : CardFrozenState()
|
||||
data object Unfrozen : CardFrozenState()
|
||||
}
|
||||
|
||||
internal data class AddToWalletBlockState(
|
||||
val onClick: () -> Unit,
|
||||
val onClickClose: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.tangem.core.decompose.ui.UiMessageSender
|
|||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.pay.repository.CardDetailsRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||
import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetailsBlockComponent
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayCardDetailsBlockStateFactory
|
||||
|
|
@ -33,7 +33,7 @@ import javax.inject.Inject
|
|||
internal class TangemPayCardDetailsBlockModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val cardDetailsRepository: CardDetailsRepository,
|
||||
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
private val cardDetailsEventListener: CardDetailsEventListener,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.domain.pay.model.SetPinResult
|
||||
import com.tangem.domain.pay.repository.CardDetailsRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||
import com.tangem.features.tangempay.entity.TangemPayChangePinUM
|
||||
import kotlinx.coroutines.flow.*
|
||||
import com.tangem.features.tangempay.model.transformers.PinCodeChangeTransformer
|
||||
|
|
@ -22,7 +22,7 @@ import javax.inject.Inject
|
|||
internal class TangemPayChangePinModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val cardDetailsRepository: CardDetailsRepository,
|
||||
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||
) : Model() {
|
||||
|
||||
val uiState: StateFlow<TangemPayChangePinUM>
|
||||
|
|
|
|||
|
|
@ -12,24 +12,25 @@ 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.components.containers.pullToRefresh.PullToRefreshConfig.ShowRefreshState
|
||||
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.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.model.TangemPayCardBalance
|
||||
import com.tangem.domain.pay.repository.CardDetailsRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.features.tangempay.components.AddFundsListener
|
||||
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsStateFactory
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||
import com.tangem.features.tangempay.model.listener.CardDetailsEvent
|
||||
import com.tangem.features.tangempay.model.listener.CardDetailsEventListener
|
||||
import com.tangem.features.tangempay.model.transformers.DetailsAddToWalletBannerTransformer
|
||||
import com.tangem.features.tangempay.model.transformers.DetailsBalanceTransformer
|
||||
import com.tangem.features.tangempay.model.transformers.TangemPayDetailsRefreshTransformer
|
||||
import com.tangem.features.tangempay.model.transformers.*
|
||||
import com.tangem.features.tangempay.navigation.TangemPayDetailsInnerRoute
|
||||
import com.tangem.features.tangempay.utils.TangemPayErrorMessageFactory
|
||||
import com.tangem.features.tangempay.utils.TangemPayMessagesFactory
|
||||
import com.tangem.features.tangempay.utils.TangemPayTxHistoryUiActions
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
|
|
@ -48,7 +49,7 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val cardDetailsRepository: CardDetailsRepository,
|
||||
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
private val cardDetailsEventListener: CardDetailsEventListener,
|
||||
) : Model(), TangemPayTxHistoryUiActions, AddFundsListener {
|
||||
|
|
@ -60,7 +61,9 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
onRefresh = ::onRefreshSwipe,
|
||||
onAddFunds = ::onClickAddFunds,
|
||||
onClickChangePin = ::onClickChangePin,
|
||||
isCardFrozen = params.config.isCardFrozen,
|
||||
onClickFreezeCard = ::onClickFreezeCard,
|
||||
onClickUnfreezeCard = ::onClickUnfreezeCard,
|
||||
)
|
||||
|
||||
val uiState: StateFlow<TangemPayDetailsUM>
|
||||
|
|
@ -84,7 +87,65 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onClickFreezeCard() {
|
||||
// TODO [REDACTED_JIRA]
|
||||
uiMessageSender.send(TangemPayMessagesFactory.createFreezeCardMessage(onFreezeClicked = ::freezeCard))
|
||||
}
|
||||
|
||||
private fun onClickUnfreezeCard() {
|
||||
uiMessageSender.send(TangemPayMessagesFactory.createUnfreezeCardMessage(onUnfreezeClicked = ::unfreezeCard))
|
||||
}
|
||||
|
||||
private fun freezeCard() {
|
||||
modelScope.launch {
|
||||
uiState.update(TangemPayFreezeUnfreezeProcessTransformer)
|
||||
cardDetailsRepository.freezeCard(cardId = params.config.cardId)
|
||||
.onLeft {
|
||||
uiMessageSender.send(SnackbarMessage(resourceReference(R.string.tangem_pay_freeze_card_failed)))
|
||||
uiState.update(
|
||||
TangemPayFreezeUnfreezeStateTransformer(
|
||||
frozen = false,
|
||||
onFreezeClick = ::onClickFreezeCard,
|
||||
onUnfreezeClick = ::onClickUnfreezeCard,
|
||||
),
|
||||
)
|
||||
}
|
||||
.onRight {
|
||||
uiMessageSender.send(SnackbarMessage(resourceReference(R.string.tangem_pay_freeze_card_success)))
|
||||
uiState.update(
|
||||
TangemPayFreezeUnfreezeStateTransformer(
|
||||
frozen = true,
|
||||
onFreezeClick = ::onClickFreezeCard,
|
||||
onUnfreezeClick = ::onClickUnfreezeCard,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfreezeCard() {
|
||||
modelScope.launch {
|
||||
uiState.update(TangemPayFreezeUnfreezeProcessTransformer)
|
||||
cardDetailsRepository.unfreezeCard(cardId = params.config.cardId)
|
||||
.onLeft {
|
||||
uiMessageSender.send(SnackbarMessage(resourceReference(R.string.tangem_pay_unfreeze_card_failed)))
|
||||
uiState.update(
|
||||
TangemPayFreezeUnfreezeStateTransformer(
|
||||
frozen = true,
|
||||
onFreezeClick = ::onClickFreezeCard,
|
||||
onUnfreezeClick = ::onClickUnfreezeCard,
|
||||
),
|
||||
)
|
||||
}
|
||||
.onRight {
|
||||
uiMessageSender.send(SnackbarMessage(resourceReference(R.string.tangem_pay_unfreeze_card_success)))
|
||||
uiState.update(
|
||||
TangemPayFreezeUnfreezeStateTransformer(
|
||||
frozen = false,
|
||||
onFreezeClick = ::onClickFreezeCard,
|
||||
onUnfreezeClick = ::onClickUnfreezeCard,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onClickAddFunds() {
|
||||
|
|
@ -188,6 +249,6 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun showBottomSheetError(type: TangemPayDetailsErrorType) {
|
||||
uiMessageSender.send(message = TangemPayErrorMessageFactory.createErrorMessage(type = type))
|
||||
uiMessageSender.send(message = TangemPayMessagesFactory.createErrorMessage(type = type))
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,10 @@ internal class DetailsBalanceTransformer(
|
|||
override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM {
|
||||
val balance = when (balance) {
|
||||
is Either.Left<UniversalError> -> {
|
||||
TangemPayDetailsBalanceBlockState.Error(persistentListOf())
|
||||
TangemPayDetailsBalanceBlockState.Error(
|
||||
actionButtons = persistentListOf(),
|
||||
frozenState = prevState.balanceBlockState.frozenState,
|
||||
)
|
||||
}
|
||||
is Either.Right<TangemPayCardBalance> -> {
|
||||
TangemPayDetailsBalanceBlockState.Content(
|
||||
|
|
@ -27,6 +30,7 @@ internal class DetailsBalanceTransformer(
|
|||
// TODO [REDACTED_TASK_KEY]: Add crypto balance when the BFF is ready
|
||||
cryptoBalance = "",
|
||||
actionButtons = prevState.balanceBlockState.actionButtons,
|
||||
frozenState = prevState.balanceBlockState.frozenState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.features.tangempay.details.impl.R
|
|||
import com.tangem.features.tangempay.entity.TangemPayAddFundsItemUM
|
||||
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
|
||||
import com.tangem.features.tangempay.utils.TangemPayErrorMessageFactory
|
||||
import com.tangem.features.tangempay.utils.TangemPayMessagesFactory
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ internal class TangemPayAddFundsUMConverter(
|
|||
TangemPayAddFundsUM(
|
||||
items = persistentListOf(),
|
||||
dismiss = listener::onDismissAddFunds,
|
||||
errorMessage = TangemPayErrorMessageFactory.createErrorMessage(
|
||||
errorMessage = TangemPayMessagesFactory.createErrorMessage(
|
||||
type = TangemPayDetailsErrorType.Receive,
|
||||
).messageBottomSheetUMV2,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.features.tangempay.model.transformers
|
||||
|
||||
import com.tangem.features.tangempay.entity.CardFrozenState
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal object TangemPayFreezeUnfreezeProcessTransformer : Transformer<TangemPayDetailsUM> {
|
||||
override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM {
|
||||
val newBalanceBlockState = when (val balanceState = prevState.balanceBlockState) {
|
||||
is TangemPayDetailsBalanceBlockState.Error -> balanceState.copy(frozenState = CardFrozenState.Pending)
|
||||
is TangemPayDetailsBalanceBlockState.Loading -> balanceState.copy(frozenState = CardFrozenState.Pending)
|
||||
is TangemPayDetailsBalanceBlockState.Content -> balanceState.copy(
|
||||
isBalanceFlickering = true,
|
||||
frozenState = CardFrozenState.Pending,
|
||||
)
|
||||
}
|
||||
|
||||
return prevState.copy(balanceBlockState = newBalanceBlockState)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package com.tangem.features.tangempay.model.transformers
|
||||
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.themedColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.*
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal class TangemPayFreezeUnfreezeStateTransformer(
|
||||
private val frozen: Boolean,
|
||||
private val onFreezeClick: () -> Unit,
|
||||
private val onUnfreezeClick: () -> Unit,
|
||||
) : Transformer<TangemPayDetailsUM> {
|
||||
|
||||
private val cardFrozenState = if (frozen) CardFrozenState.Frozen(onUnfreezeClick) else CardFrozenState.Unfrozen
|
||||
private val removeType =
|
||||
if (frozen) TangemPayDetailsTopBarMenuItemType.FreezeCard else TangemPayDetailsTopBarMenuItemType.UnfreezeCard
|
||||
private val addType =
|
||||
if (frozen) TangemPayDetailsTopBarMenuItemType.UnfreezeCard else TangemPayDetailsTopBarMenuItemType.FreezeCard
|
||||
|
||||
override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM {
|
||||
val dropdownMenuItems = createUpdatedMenuItems(prevState.topBarConfig.items)
|
||||
val newBalanceBlockState = createUpdatedBalanceState(prevState.balanceBlockState)
|
||||
|
||||
return prevState.copy(
|
||||
topBarConfig = prevState.topBarConfig.copy(items = dropdownMenuItems),
|
||||
balanceBlockState = newBalanceBlockState,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createUpdatedMenuItems(
|
||||
items: ImmutableList<TangemPayDetailsTopBarMenuItem>?,
|
||||
): ImmutableList<TangemPayDetailsTopBarMenuItem>? {
|
||||
return items
|
||||
?.filter { it.type != removeType }
|
||||
?.plus(createMenuItemToAdd())
|
||||
?.toPersistentList()
|
||||
}
|
||||
|
||||
private fun createMenuItemToAdd(): TangemPayDetailsTopBarMenuItem {
|
||||
return TangemPayDetailsTopBarMenuItem(
|
||||
type = addType,
|
||||
dropdownItem = TangemDropdownMenuItem(
|
||||
title = resourceReference(
|
||||
id = if (frozen) {
|
||||
R.string.tangempay_card_details_unfreeze_card
|
||||
} else {
|
||||
R.string.tangempay_card_details_freeze_card
|
||||
},
|
||||
),
|
||||
textColor = themedColor { TangemTheme.colors.text.primary1 },
|
||||
onClick = if (frozen) onUnfreezeClick else onFreezeClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createUpdatedBalanceState(
|
||||
balanceState: TangemPayDetailsBalanceBlockState,
|
||||
): TangemPayDetailsBalanceBlockState {
|
||||
return when (balanceState) {
|
||||
is TangemPayDetailsBalanceBlockState.Error -> balanceState.copy(frozenState = cardFrozenState)
|
||||
is TangemPayDetailsBalanceBlockState.Loading -> balanceState.copy(frozenState = cardFrozenState)
|
||||
is TangemPayDetailsBalanceBlockState.Content -> balanceState.copy(
|
||||
isBalanceFlickering = false,
|
||||
frozenState = cardFrozenState,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||
import androidx.compose.ui.res.vectorResource
|
||||
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
|
||||
|
|
@ -70,7 +71,7 @@ internal fun TangemPayAddFundsContent(state: TangemPayAddFundsUM) {
|
|||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
state.items.forEach {
|
||||
state.items.fastForEach {
|
||||
key(it.title) {
|
||||
TangemPayTopUpItem(state = it)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
|||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
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.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
|
@ -20,6 +22,7 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.buttons.HorizontalActionChips
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
|
|
@ -27,9 +30,12 @@ import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfi
|
|||
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.snackbar.TangemSnackbarHost
|
||||
import com.tangem.core.ui.components.text.applyBladeBrush
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TokenDetailsTopBarTestTags
|
||||
|
|
@ -48,19 +54,12 @@ internal fun TangemPayDetailsScreen(
|
|||
txHistoryComponent: TangemPayTxHistoryComponent,
|
||||
cardDetailsBlockComponent: TangemPayCardDetailsBlockComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = { TangemPayDetailsTopAppBar(config = state.topBarConfig) },
|
||||
contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.navigationBars),
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
snackbarHost = {
|
||||
TangemSnackbarHost(
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
hostState = snackbarHostState,
|
||||
)
|
||||
},
|
||||
) { scaffoldPaddings ->
|
||||
val listState = rememberLazyListState()
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
|
@ -106,6 +105,20 @@ internal fun TangemPayDetailsScreen(
|
|||
item(TangemPayCardDetailsUM::class.java) {
|
||||
TangemPayCardDetailsBlockItem(component = cardDetailsBlockComponent, state = cardDetailsState)
|
||||
}
|
||||
when (val cardFrozenState = state.balanceBlockState.frozenState) {
|
||||
is CardFrozenState.Frozen -> item(CardFrozenState.Frozen::class.java) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.animateItem()
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(top = 12.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResourceSafe(R.string.tangempay_card_details_unfreeze_card),
|
||||
onClick = cardFrozenState.onUnfreeze,
|
||||
)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryState) }
|
||||
}
|
||||
}
|
||||
|
|
@ -127,14 +140,22 @@ internal fun TangemPayDetailsBalanceBlock(
|
|||
)
|
||||
.padding(vertical = 12.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
text = stringResourceSafe(R.string.tangempay_title),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.tangempay_title),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
TangemPayCardStatus(state = state.frozenState)
|
||||
}
|
||||
FiatBalance(
|
||||
modifier = Modifier.padding(start = 12.dp, top = 4.dp),
|
||||
modifier = Modifier.padding(start = 12.dp, top = 8.dp),
|
||||
state = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
|
|
@ -155,6 +176,36 @@ internal fun TangemPayDetailsBalanceBlock(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemPayCardStatus(state: CardFrozenState, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is CardFrozenState.Unfrozen -> Unit
|
||||
is CardFrozenState.Pending -> CircularProgressIndicator(
|
||||
modifier = modifier.size(18.dp),
|
||||
trackColor = Color.Transparent,
|
||||
color = TangemColorPalette.Azure,
|
||||
strokeWidth = 2.dp,
|
||||
)
|
||||
is CardFrozenState.Frozen -> Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_snow_24),
|
||||
tint = TangemTheme.colors.text.disabled,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.tangem_pay_card_frozen),
|
||||
color = TangemTheme.colors.text.disabled,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FiatBalance(
|
||||
state: TangemPayDetailsBalanceBlockState,
|
||||
|
|
@ -173,18 +224,30 @@ private fun FiatBalance(
|
|||
text = state.fiatBalance.orMaskWithStars(isBalanceHidden),
|
||||
style = TangemTheme.typography.h2.applyBladeBrush(
|
||||
isEnabled = state.isBalanceFlickering,
|
||||
textColor = TangemTheme.colors.text.primary1,
|
||||
textColor = getTextColor(
|
||||
isCardFrozen = state.frozenState is CardFrozenState.Frozen,
|
||||
defaultColor = TangemTheme.colors.text.primary1,
|
||||
),
|
||||
),
|
||||
)
|
||||
is TangemPayDetailsBalanceBlockState.Error -> Text(
|
||||
modifier = modifier,
|
||||
text = DASH_SIGN.orMaskWithStars(isBalanceHidden),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
color = getTextColor(
|
||||
isCardFrozen = state.frozenState is CardFrozenState.Frozen,
|
||||
defaultColor = TangemTheme.colors.text.primary1,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
private fun getTextColor(isCardFrozen: Boolean, defaultColor: Color): Color {
|
||||
return if (isCardFrozen) TangemTheme.colors.text.tertiary else defaultColor
|
||||
}
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
@Composable
|
||||
private fun CryptoBalance(
|
||||
|
|
@ -252,7 +315,7 @@ private fun TangemPayDetailsTopAppBar(config: TangemPayDetailsTopBarConfig, modi
|
|||
content = {
|
||||
config.items?.fastForEach {
|
||||
TangemDropdownItem(
|
||||
item = it,
|
||||
item = it.dropdownItem,
|
||||
dismissParent = { showDropdownMenu = false },
|
||||
)
|
||||
}
|
||||
|
|
@ -310,6 +373,7 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<Ta
|
|||
cryptoBalance = "1234.56 USDT",
|
||||
fiatBalance = "$1234.56",
|
||||
isBalanceFlickering = false,
|
||||
frozenState = CardFrozenState.Frozen(onUnfreeze = {}),
|
||||
),
|
||||
addToWalletBlockState = AddToWalletBlockState({}, {}),
|
||||
isBalanceHidden = false,
|
||||
|
|
@ -318,7 +382,10 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<Ta
|
|||
TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(onBackClick = {}, items = null),
|
||||
pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = {}),
|
||||
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(actionButtons = persistentListOf()),
|
||||
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(
|
||||
actionButtons = persistentListOf(),
|
||||
frozenState = CardFrozenState.Pending,
|
||||
),
|
||||
addToWalletBlockState = AddToWalletBlockState({}, {}),
|
||||
isBalanceHidden = false,
|
||||
addFundsEnabled = true,
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
package com.tangem.features.tangempay.utils
|
||||
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.BottomSheetMessageV2
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
|
||||
|
||||
internal object TangemPayErrorMessageFactory {
|
||||
|
||||
fun createErrorMessage(type: TangemPayDetailsErrorType): BottomSheetMessageV2 {
|
||||
return when (type) {
|
||||
TangemPayDetailsErrorType.Receive -> bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_attention_20) {
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Attention
|
||||
}
|
||||
title = TextReference.Res(R.string.tangempay_card_details_receive_error_title)
|
||||
body = TextReference.Res(R.string.tangempay_card_details_receive_error_description)
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.common_got_it)
|
||||
onClick { closeBs() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package com.tangem.features.tangempay.utils
|
||||
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.BottomSheetMessageV2
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
|
||||
|
||||
internal object TangemPayMessagesFactory {
|
||||
|
||||
fun createErrorMessage(type: TangemPayDetailsErrorType): BottomSheetMessageV2 {
|
||||
return when (type) {
|
||||
TangemPayDetailsErrorType.Receive -> bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_attention_20) {
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Attention
|
||||
}
|
||||
title = TextReference.Res(R.string.tangempay_card_details_receive_error_title)
|
||||
body = TextReference.Res(R.string.tangempay_card_details_receive_error_description)
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.common_got_it)
|
||||
onClick { closeBs() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createFreezeCardMessage(onFreezeClicked: () -> Unit): BottomSheetMessageV2 {
|
||||
return bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.ic_snow_24) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Accent
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Accent
|
||||
}
|
||||
title = TextReference.Res(R.string.tangem_pay_freeze_card_alert_title)
|
||||
body = TextReference.Res(R.string.tangem_pay_freeze_card_alert_body)
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.tangem_pay_freeze_card_freeze)
|
||||
onClick {
|
||||
onFreezeClicked()
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createUnfreezeCardMessage(onUnfreezeClicked: () -> Unit): BottomSheetMessageV2 {
|
||||
return bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.ic_snow_24) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Accent
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Accent
|
||||
}
|
||||
title = TextReference.Res(R.string.tangem_pay_unfreeze_card_alert_title)
|
||||
body = TextReference.Res(R.string.tangem_pay_unfreeze_card_alert_body)
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.tangempay_card_details_unfreeze_card)
|
||||
onClick {
|
||||
onUnfreezeClicked()
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
|||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
|
||||
import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
|
||||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus.CANCELED
|
||||
import com.tangem.domain.pay.model.OrderStatus.UNKNOWN
|
||||
|
|
@ -35,29 +36,36 @@ internal class TangemPayInitialStateTransformer(
|
|||
|
||||
private fun createInitialState(): TangemPayState {
|
||||
val cardInfo = value?.info?.cardInfo
|
||||
val productInstance = value?.info?.productInstance
|
||||
return when {
|
||||
value == null -> TangemPayState.Empty
|
||||
!value.info.isKycApproved -> createKycInProgressState(onClickKyc)
|
||||
cardInfo != null -> getCardInfoState(cardInfo)
|
||||
cardInfo != null && productInstance != null -> getCardInfoState(cardInfo, productInstance)
|
||||
value.orderStatus == UNKNOWN || value.orderStatus == CANCELED -> createIssueAvailableState(onClickIssue)
|
||||
else -> createIssueProgressState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCardInfoState(cardInfo: CardInfo): TangemPayState = TangemPayState.Card(
|
||||
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
|
||||
balanceText = TextReference.Str(getBalanceText(cardInfo)),
|
||||
onClick = {
|
||||
openDetails(
|
||||
TangemPayDetailsConfig(
|
||||
customerWalletAddress = cardInfo.customerWalletAddress,
|
||||
cardNumberEnd = cardInfo.lastFourDigits,
|
||||
chainId = POLYGON_CHAIN_ID,
|
||||
depositAddress = cardInfo.depositAddress,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
private fun getCardInfoState(cardInfo: CardInfo, productInstance: ProductInstance): TangemPayState =
|
||||
TangemPayState.Card(
|
||||
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
|
||||
balanceText = TextReference.Str(getBalanceText(cardInfo)),
|
||||
onClick = {
|
||||
openDetails(
|
||||
TangemPayDetailsConfig(
|
||||
cardId = productInstance.cardId,
|
||||
isCardFrozen = when (productInstance.status) {
|
||||
ProductInstance.Status.ACTIVE -> false
|
||||
ProductInstance.Status.INACTIVE -> true
|
||||
},
|
||||
customerWalletAddress = cardInfo.customerWalletAddress,
|
||||
cardNumberEnd = cardInfo.lastFourDigits,
|
||||
chainId = POLYGON_CHAIN_ID,
|
||||
depositAddress = cardInfo.depositAddress,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
private fun getBalanceText(cardInfo: CardInfo): String {
|
||||
val currency = Currency.getInstance(cardInfo.currencyCode)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue