Updated on 2026-08-14
This commit is contained in:
parent
507b608807
commit
768422ea7c
34 changed files with 661 additions and 80 deletions
|
|
@ -4,6 +4,7 @@ import com.tangem.core.decompose.factory.ComponentFactory
|
|||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import java.math.BigDecimal
|
||||
|
||||
interface SwapComponent : ComposableContentComponent {
|
||||
|
||||
|
|
@ -13,7 +14,14 @@ interface SwapComponent : ComposableContentComponent {
|
|||
val userWalletId: UserWalletId,
|
||||
val isInitialReverseOrder: Boolean = false,
|
||||
val screenSource: String,
|
||||
)
|
||||
val tangemPayInput: TangemPayInput? = null,
|
||||
) {
|
||||
data class TangemPayInput(
|
||||
val cryptoAmount: BigDecimal,
|
||||
val fiatAmount: BigDecimal,
|
||||
val depositAddress: String,
|
||||
)
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, SwapComponent>
|
||||
}
|
||||
|
|
@ -46,6 +46,7 @@ dependencies {
|
|||
implementation(projects.domain.express.models)
|
||||
implementation(projects.domain.account)
|
||||
implementation(projects.domain.account.status)
|
||||
implementation(projects.domain.visa)
|
||||
|
||||
/** Feature modules */
|
||||
implementation(projects.features.swap.domain)
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import com.tangem.domain.promo.models.StoryContentIds
|
|||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||
import com.tangem.domain.settings.usercountry.models.UserCountry
|
||||
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
|
||||
import com.tangem.domain.tangempay.GetTangemPayCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
|
||||
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
|
|
@ -113,6 +114,7 @@ internal class SwapModel @Inject constructor(
|
|||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val getTangemPayCurrencyStatusUseCase: GetTangemPayCurrencyStatusUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<SwapComponent.Params>()
|
||||
|
|
@ -121,6 +123,7 @@ internal class SwapModel @Inject constructor(
|
|||
private val initialCurrencyTo = params.currencyTo
|
||||
private val userWalletId = params.userWalletId
|
||||
private val isInitiallyReversed = params.isInitialReverseOrder
|
||||
private val tangemPayInput = params.tangemPayInput
|
||||
|
||||
private val userWallet by lazy {
|
||||
requireNotNull(
|
||||
|
|
@ -196,7 +199,7 @@ internal class SwapModel @Inject constructor(
|
|||
}
|
||||
|
||||
modelScope.launch(dispatchers.io) {
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
if (accountsFeatureToggles.isFeatureEnabled && tangemPayInput == null) {
|
||||
isAccountsMode = isAccountsModeEnabledUseCase.invokeSync()
|
||||
|
||||
val fromAccountStatus = getAccountCurrencyStatusUseCase.invokeSync(
|
||||
|
|
@ -220,10 +223,19 @@ internal class SwapModel @Inject constructor(
|
|||
initTokens(isInitiallyReversed)
|
||||
}
|
||||
} else {
|
||||
val fromStatus = getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = initialCurrencyFrom.id,
|
||||
).getOrNull()
|
||||
val fromStatus = if (tangemPayInput != null) {
|
||||
getTangemPayCurrencyStatusUseCase(
|
||||
currency = initialCurrencyFrom,
|
||||
cryptoAmount = tangemPayInput.cryptoAmount,
|
||||
fiatAmount = tangemPayInput.fiatAmount,
|
||||
depositAddress = tangemPayInput.depositAddress,
|
||||
)
|
||||
} else {
|
||||
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = initialCurrencyFrom.id,
|
||||
).getOrNull()
|
||||
}
|
||||
val toStatus = initialCurrencyTo?.let {
|
||||
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
|
||||
userWalletId = userWalletId,
|
||||
|
|
@ -555,6 +567,7 @@ internal class SwapModel @Inject constructor(
|
|||
selectedFeeType = dataState.selectedFee?.feeType ?: FeeType.NORMAL,
|
||||
isReverseSwapPossible = isReverseSwapPossible(),
|
||||
needApplyFCARestrictions = userCountry.needApplyFCARestrictions(),
|
||||
isForTangemPaySwap = tangemPayInput != null,
|
||||
)
|
||||
if (uiState.notifications.any { it is SwapNotificationUM.Error.UnableToCoverFeeWarning }) {
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -574,6 +587,7 @@ internal class SwapModel @Inject constructor(
|
|||
toTokenStatus = toTokenStatus,
|
||||
isReverseSwapPossible = isReverseSwapPossible(),
|
||||
toAccount = dataState.toAccount,
|
||||
isForTangemPaySwap = tangemPayInput != null,
|
||||
)
|
||||
}
|
||||
is SwapState.SwapError -> {
|
||||
|
|
@ -588,6 +602,7 @@ internal class SwapModel @Inject constructor(
|
|||
isReverseSwapPossible = isReverseSwapPossible(),
|
||||
needApplyFCARestrictions = userCountry.needApplyFCARestrictions(),
|
||||
toAccount = dataState.toAccount,
|
||||
isForTangemPaySwap = tangemPayInput != null,
|
||||
)
|
||||
sendErrorAnalyticsEvent(state.error, provider)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -277,6 +277,7 @@ internal class StateBuilder(
|
|||
selectedFeeType: FeeType,
|
||||
isReverseSwapPossible: Boolean,
|
||||
needApplyFCARestrictions: Boolean,
|
||||
isForTangemPaySwap: Boolean,
|
||||
): SwapStateHolder {
|
||||
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
|
|
@ -369,7 +370,7 @@ internal class StateBuilder(
|
|||
enabled = getSwapButtonEnabled(notifications),
|
||||
onClick = actions.onSwapClick,
|
||||
),
|
||||
changeCardsButtonState = if (isReverseSwapPossible) {
|
||||
changeCardsButtonState = if (isReverseSwapPossible && !isForTangemPaySwap) {
|
||||
ChangeCardsButtonState.ENABLED
|
||||
} else {
|
||||
ChangeCardsButtonState.DISABLED
|
||||
|
|
@ -442,6 +443,7 @@ internal class StateBuilder(
|
|||
expressDataError: ExpressDataError,
|
||||
isReverseSwapPossible: Boolean,
|
||||
needApplyFCARestrictions: Boolean,
|
||||
isForTangemPaySwap: Boolean,
|
||||
): SwapStateHolder {
|
||||
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
|
|
@ -500,7 +502,7 @@ internal class StateBuilder(
|
|||
enabled = false,
|
||||
onClick = actions.onSwapClick,
|
||||
),
|
||||
changeCardsButtonState = if (isReverseSwapPossible) {
|
||||
changeCardsButtonState = if (isReverseSwapPossible && !isForTangemPaySwap) {
|
||||
ChangeCardsButtonState.ENABLED
|
||||
} else {
|
||||
ChangeCardsButtonState.DISABLED
|
||||
|
|
@ -559,6 +561,7 @@ internal class StateBuilder(
|
|||
toTokenStatus: CryptoCurrencyStatus?,
|
||||
toAccount: Account.CryptoPortfolio?,
|
||||
isReverseSwapPossible: Boolean,
|
||||
isForTangemPaySwap: Boolean,
|
||||
): SwapStateHolder {
|
||||
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
|
|
@ -601,7 +604,7 @@ internal class StateBuilder(
|
|||
enabled = false,
|
||||
onClick = { },
|
||||
),
|
||||
changeCardsButtonState = if (isReverseSwapPossible) {
|
||||
changeCardsButtonState = if (isReverseSwapPossible && !isForTangemPaySwap) {
|
||||
ChangeCardsButtonState.ENABLED
|
||||
} else {
|
||||
ChangeCardsButtonState.DISABLED
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@ dependencies {
|
|||
implementation(projects.core.error)
|
||||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.analytics.models)
|
||||
|
||||
/** Common */
|
||||
implementation(projects.common.ui)
|
||||
implementation(projects.common.routing)
|
||||
|
||||
/** Features api */
|
||||
implementation(projects.features.tangempay.details.api)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.features.tangempay.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
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.TangemPayTopUpData
|
||||
import com.tangem.features.tangempay.model.TangemPayAddFundsModel
|
||||
import com.tangem.features.tangempay.ui.TangemPayAddFundsContent
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TangemPayAddFundsComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: Params,
|
||||
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: TangemPayAddFundsModel = getOrCreateModel(params = params)
|
||||
|
||||
override fun dismiss() {
|
||||
model.onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
TangemPayAddFundsContent(state = model.uiState)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val listener: AddFundsListener,
|
||||
val walletId: UserWalletId,
|
||||
val cryptoBalance: BigDecimal,
|
||||
val fiatBalance: BigDecimal,
|
||||
val depositAddress: String,
|
||||
val chainId: Int,
|
||||
)
|
||||
}
|
||||
|
||||
internal interface AddFundsListener {
|
||||
fun onClickReceive(data: TangemPayTopUpData)
|
||||
fun onClickSwap(data: TangemPayTopUpData)
|
||||
fun onDismissAddFunds()
|
||||
}
|
||||
|
|
@ -87,6 +87,17 @@ internal class TangemPayDetailsComponent(
|
|||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
is TangemPayDetailsNavigation.AddFunds -> TangemPayAddFundsComponent(
|
||||
appComponentContext = context,
|
||||
params = TangemPayAddFundsComponent.Params(
|
||||
walletId = navigation.walletId,
|
||||
cryptoBalance = navigation.cryptoBalance,
|
||||
fiatBalance = navigation.fiatBalance,
|
||||
depositAddress = navigation.depositAddress,
|
||||
chainId = navigation.chainId,
|
||||
listener = model,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.tangempay.di
|
|||
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.tangempay.model.TangemPayAddFundsModel
|
||||
import com.tangem.features.tangempay.model.TangemPayAddToWalletModel
|
||||
import com.tangem.features.tangempay.model.TangemPayCardDetailsBlockModel
|
||||
import com.tangem.features.tangempay.model.TangemPayChangePinModel
|
||||
|
|
@ -47,4 +48,9 @@ internal interface TangemPayModelModule {
|
|||
@IntoMap
|
||||
@ClassKey(TangemPayAddToWalletModel::class)
|
||||
fun bindTangemPayAddToWalletModel(model: TangemPayAddToWalletModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(TangemPayAddFundsModel::class)
|
||||
fun bindTangemPayAddFundsModel(model: TangemPayAddFundsModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class TangemPayAddFundsUM(
|
||||
val items: ImmutableList<TangemPayAddFundsItemUM>,
|
||||
val dismiss: () -> Unit,
|
||||
val errorMessage: MessageBottomSheetUMV2?,
|
||||
)
|
||||
|
||||
internal data class TangemPayAddFundsItemUM(
|
||||
@DrawableRes val iconRes: Int,
|
||||
val title: TextReference,
|
||||
val description: TextReference,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.serialization.SerializedBigDecimal
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -10,6 +12,15 @@ internal sealed class TangemPayDetailsNavigation {
|
|||
@Serializable
|
||||
data class Receive(val config: TokenReceiveConfig) : TangemPayDetailsNavigation()
|
||||
|
||||
@Serializable
|
||||
data class AddFunds(
|
||||
val walletId: UserWalletId,
|
||||
val cryptoBalance: SerializedBigDecimal,
|
||||
val fiatBalance: SerializedBigDecimal,
|
||||
val depositAddress: String,
|
||||
val chainId: Int,
|
||||
) : TangemPayDetailsNavigation()
|
||||
|
||||
@Serializable
|
||||
data class TransactionDetails(val transaction: TangemPayTxHistoryItem) : TangemPayDetailsNavigation()
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
internal class TangemPayDetailsStateFactory(
|
||||
private val onBack: () -> Unit,
|
||||
private val onRefresh: (ShowRefreshState) -> Unit,
|
||||
private val onReceive: () -> Unit,
|
||||
private val onAddFunds: () -> Unit,
|
||||
private val onClickChangePin: () -> Unit,
|
||||
private val onClickFreezeCard: () -> Unit,
|
||||
) {
|
||||
|
|
@ -39,13 +39,14 @@ internal class TangemPayDetailsStateFactory(
|
|||
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(
|
||||
actionButtons = persistentListOf(
|
||||
ActionButtonConfig(
|
||||
text = resourceReference(id = R.string.common_receive),
|
||||
text = resourceReference(id = R.string.tangempay_card_details_add_funds),
|
||||
iconResId = R.drawable.ic_arrow_down_24,
|
||||
onClick = onReceive,
|
||||
onClick = onAddFunds,
|
||||
),
|
||||
),
|
||||
),
|
||||
addToWalletBlockState = null,
|
||||
isBalanceHidden = false,
|
||||
addFundsEnabled = true,
|
||||
)
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ internal data class TangemPayDetailsUM(
|
|||
val balanceBlockState: TangemPayDetailsBalanceBlockState,
|
||||
val addToWalletBlockState: AddToWalletBlockState?,
|
||||
val isBalanceHidden: Boolean,
|
||||
val addFundsEnabled: Boolean,
|
||||
)
|
||||
|
||||
internal data class TangemPayCardDetailsUM(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.features.tangempay.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.pay.TangemPayTopUpDataFactory
|
||||
import com.tangem.features.tangempay.components.TangemPayAddFundsComponent
|
||||
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
|
||||
import com.tangem.features.tangempay.model.transformers.TangemPayAddFundsUMConverter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class TangemPayAddFundsModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val tangemPayTopUpDataFactory: TangemPayTopUpDataFactory,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<TangemPayAddFundsComponent.Params>()
|
||||
|
||||
val uiState: TangemPayAddFundsUM = getInitialState()
|
||||
|
||||
private fun getInitialState(): TangemPayAddFundsUM {
|
||||
val data = tangemPayTopUpDataFactory.create(
|
||||
depositAddress = params.depositAddress,
|
||||
chainId = params.chainId,
|
||||
cryptoBalance = params.cryptoBalance,
|
||||
fiatBalance = params.fiatBalance,
|
||||
).getOrNull()
|
||||
|
||||
return TangemPayAddFundsUMConverter(listener = params.listener).convert(data)
|
||||
}
|
||||
|
||||
fun onDismiss() {
|
||||
params.listener.onDismissAddFunds()
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,9 @@ package com.tangem.features.tangempay.model
|
|||
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.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -10,9 +13,11 @@ 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.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.pay.DataForReceiveFactory
|
||||
import com.tangem.domain.pay.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.model.TangemPayCardBalance
|
||||
import com.tangem.domain.pay.repository.CardDetailsRepository
|
||||
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.entity.TangemPayDetailsErrorType
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
|
||||
|
|
@ -44,17 +49,16 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val cardDetailsRepository: CardDetailsRepository,
|
||||
private val dataForReceiveFactory: DataForReceiveFactory,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
private val cardDetailsEventListener: CardDetailsEventListener,
|
||||
) : Model(), TangemPayTxHistoryUiActions {
|
||||
) : Model(), TangemPayTxHistoryUiActions, AddFundsListener {
|
||||
|
||||
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
|
||||
|
||||
private val stateFactory = TangemPayDetailsStateFactory(
|
||||
onBack = router::pop,
|
||||
onRefresh = ::onRefreshSwipe,
|
||||
onReceive = ::onClickReceive,
|
||||
onAddFunds = ::onClickAddFunds,
|
||||
onClickChangePin = ::onClickChangePin,
|
||||
onClickFreezeCard = ::onClickFreezeCard,
|
||||
)
|
||||
|
|
@ -66,6 +70,8 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
private val fetchBalanceJobHolder = JobHolder()
|
||||
private val addToWalletBannerJobHolder = JobHolder()
|
||||
|
||||
private var balance: TangemPayCardBalance? = null
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<TangemPayDetailsNavigation> = SlotNavigation()
|
||||
|
||||
init {
|
||||
|
|
@ -81,30 +87,27 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
// TODO [REDACTED_JIRA]
|
||||
}
|
||||
|
||||
private fun onClickReceive() {
|
||||
val depositAddress = params.config.depositAddress
|
||||
if (depositAddress == null) {
|
||||
private fun onClickAddFunds() {
|
||||
if (params.config.depositAddress == null || balance == null) {
|
||||
showBottomSheetError(TangemPayDetailsErrorType.Receive)
|
||||
} else {
|
||||
dataForReceiveFactory.getDataForReceive(depositAddress = depositAddress, chainId = params.config.chainId)
|
||||
.onRight {
|
||||
val config = TokenReceiveConfig(
|
||||
shouldShowWarning = false,
|
||||
cryptoCurrency = it.currency,
|
||||
userWalletId = it.walletId,
|
||||
showMemoDisclaimer = false,
|
||||
receiveAddress = it.receiveAddress,
|
||||
)
|
||||
bottomSheetNavigation.activate(TangemPayDetailsNavigation.Receive(config))
|
||||
}
|
||||
.onLeft { showBottomSheetError(TangemPayDetailsErrorType.Receive) }
|
||||
bottomSheetNavigation.activate(
|
||||
TangemPayDetailsNavigation.AddFunds(
|
||||
walletId = params.userWalletId,
|
||||
fiatBalance = requireNotNull(balance).balance,
|
||||
// Using the fiat balance as crypto balance. This will be changed when back returns crypto balance
|
||||
cryptoBalance = requireNotNull(balance).balance,
|
||||
depositAddress = requireNotNull(params.config.depositAddress),
|
||||
chainId = params.config.chainId,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchBalance(): Job {
|
||||
return modelScope.launch {
|
||||
val result = cardDetailsRepository.getCardBalance()
|
||||
uiState.update(DetailsBalanceTransformer(result))
|
||||
val result = cardDetailsRepository.getCardBalance().onRight { balance = it }
|
||||
uiState.update(DetailsBalanceTransformer(balance = result))
|
||||
}.saveIn(fetchBalanceJobHolder)
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +150,39 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
}.saveIn(addToWalletBannerJobHolder)
|
||||
}
|
||||
|
||||
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,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onClickReceive(data: TangemPayTopUpData) {
|
||||
bottomSheetNavigation.dismiss()
|
||||
val config = TokenReceiveConfig(
|
||||
shouldShowWarning = false,
|
||||
cryptoCurrency = data.currency,
|
||||
userWalletId = data.walletId,
|
||||
showMemoDisclaimer = false,
|
||||
receiveAddress = data.receiveAddress,
|
||||
)
|
||||
bottomSheetNavigation.activate(TangemPayDetailsNavigation.Receive(config))
|
||||
}
|
||||
|
||||
override fun onDismissAddFunds() {
|
||||
bottomSheetNavigation.dismiss()
|
||||
}
|
||||
|
||||
override fun onTransactionClick(item: TangemPayTxHistoryItem) {
|
||||
bottomSheetNavigation.activate(TangemPayDetailsNavigation.TransactionDetails(item))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tangem.features.tangempay.model.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.pay.TangemPayTopUpData
|
||||
import com.tangem.features.tangempay.components.AddFundsListener
|
||||
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.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class TangemPayAddFundsUMConverter(
|
||||
val listener: AddFundsListener,
|
||||
) : Converter<TangemPayTopUpData?, TangemPayAddFundsUM> {
|
||||
|
||||
override fun convert(value: TangemPayTopUpData?): TangemPayAddFundsUM {
|
||||
return if (value == null) {
|
||||
TangemPayAddFundsUM(
|
||||
items = persistentListOf(),
|
||||
dismiss = listener::onDismissAddFunds,
|
||||
errorMessage = TangemPayErrorMessageFactory.createErrorMessage(
|
||||
type = TangemPayDetailsErrorType.Receive,
|
||||
).messageBottomSheetUMV2,
|
||||
)
|
||||
} else {
|
||||
TangemPayAddFundsUM(
|
||||
items = persistentListOf(
|
||||
TangemPayAddFundsItemUM(
|
||||
iconRes = R.drawable.ic_exchange_vertical_24,
|
||||
title = TextReference.Res(R.string.common_exchange),
|
||||
description = TextReference.Res(R.string.tangempay_card_details_swap_description),
|
||||
onClick = { listener.onClickSwap(value) },
|
||||
),
|
||||
TangemPayAddFundsItemUM(
|
||||
iconRes = R.drawable.ic_arrow_down_24,
|
||||
title = TextReference.Res(R.string.common_receive),
|
||||
description = TextReference.Res(R.string.tangempay_card_details_receive_description),
|
||||
onClick = { listener.onClickReceive(value) },
|
||||
),
|
||||
),
|
||||
dismiss = listener::onDismissAddFunds,
|
||||
errorMessage = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
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.Icon
|
||||
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.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.MessageBottomSheetV2Content
|
||||
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.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) {
|
||||
MessageBottomSheetV2Content(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.forEach {
|
||||
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),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(16.dp),
|
||||
imageVector = ImageVector.vectorResource(id = state.iconRes),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
)
|
||||
}
|
||||
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(
|
||||
iconRes = R.drawable.ic_exchange_vertical_24,
|
||||
title = Res(R.string.common_exchange),
|
||||
description = Res(R.string.exсhange_token_description),
|
||||
onClick = {},
|
||||
),
|
||||
TangemPayAddFundsItemUM(
|
||||
iconRes = R.drawable.ic_arrow_down_24,
|
||||
title = Res(R.string.common_receive),
|
||||
description = Res(R.string.receive_token_description),
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
dismiss = {},
|
||||
errorMessage = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -313,6 +313,7 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<Ta
|
|||
),
|
||||
addToWalletBlockState = AddToWalletBlockState({}, {}),
|
||||
isBalanceHidden = false,
|
||||
addFundsEnabled = true,
|
||||
),
|
||||
TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(onBackClick = {}, items = null),
|
||||
|
|
@ -320,6 +321,7 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<Ta
|
|||
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(actionButtons = persistentListOf()),
|
||||
addToWalletBlockState = AddToWalletBlockState({}, {}),
|
||||
isBalanceHidden = false,
|
||||
addFundsEnabled = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue