Updated on 2026-08-14
This commit is contained in:
parent
c813e330c2
commit
541bea5fe0
11 changed files with 230 additions and 23 deletions
|
|
@ -7,9 +7,15 @@ interface FeeSelectorReloadTrigger {
|
|||
|
||||
/** Trigger fee update */
|
||||
suspend fun triggerUpdate(feeData: FeeSelectorData = FeeSelectorData())
|
||||
|
||||
/** Triggers fee loading status. Used in cases where fee loading state is extended due to business logic */
|
||||
suspend fun triggerLoadingState()
|
||||
}
|
||||
|
||||
interface FeeSelectorReloadListener {
|
||||
/** Flow triggers fee reload */
|
||||
val reloadTriggerFlow: Flow<FeeSelectorData>
|
||||
|
||||
/** Triggers fee loading status. Used in cases where fee loading state is extended due to business logic */
|
||||
val loadingStateTriggerFlow: Flow<Unit>
|
||||
}
|
||||
|
|
@ -20,6 +20,9 @@ internal class DefaultFeeSelectorReloadTrigger @Inject constructor() :
|
|||
override val reloadTriggerFlow: SharedFlow<FeeSelectorData>
|
||||
field = MutableSharedFlow()
|
||||
|
||||
override val loadingStateTriggerFlow: SharedFlow<Unit>
|
||||
field = MutableSharedFlow()
|
||||
|
||||
override val checkReloadTriggerFlow: SharedFlow<Unit>
|
||||
field = MutableSharedFlow()
|
||||
|
||||
|
|
@ -30,6 +33,10 @@ internal class DefaultFeeSelectorReloadTrigger @Inject constructor() :
|
|||
reloadTriggerFlow.emit(feeData)
|
||||
}
|
||||
|
||||
override suspend fun triggerLoadingState() {
|
||||
loadingStateTriggerFlow.emit(Unit)
|
||||
}
|
||||
|
||||
override suspend fun triggerCheckUpdate() {
|
||||
checkReloadTriggerFlow.emit(Unit)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ internal class FeeSelectorModel @Inject constructor(
|
|||
initAppCurrency()
|
||||
subscribeOnFeeReloadTriggerUpdates()
|
||||
subscribeOnFeeCheckReloadTriggerUpdates()
|
||||
subscribeOnFeeLoadingStateTriggerUpdates()
|
||||
loadFee()
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +69,7 @@ internal class FeeSelectorModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun loadFee() {
|
||||
uiState.update(FeeSelectorLoadingTransformers)
|
||||
uiState.update(FeeSelectorLoadingTransformer)
|
||||
modelScope.launch {
|
||||
params.onLoadFee()
|
||||
.fold(
|
||||
|
|
@ -135,6 +136,12 @@ internal class FeeSelectorModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnFeeLoadingStateTriggerUpdates() {
|
||||
feeSelectorReloadListener.loadingStateTriggerFlow
|
||||
.onEach { uiState.update(FeeSelectorLoadingTransformer) }
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnFeeCheckReloadTriggerUpdates() {
|
||||
feeSelectorCheckReloadListener.checkReloadTriggerFlow
|
||||
.onEach { checkLoadFee() }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.send.v2.feeselector.model.transformers
|
|||
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal object FeeSelectorLoadingTransformers : Transformer<FeeSelectorUM> {
|
||||
internal object FeeSelectorLoadingTransformer : Transformer<FeeSelectorUM> {
|
||||
|
||||
override fun transform(prevState: FeeSelectorUM): FeeSelectorUM {
|
||||
return FeeSelectorUM.Loading
|
||||
|
|
@ -4,6 +4,8 @@ import com.tangem.core.decompose.di.ModelComponent
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger
|
||||
import com.tangem.features.swap.v2.impl.amount.DefaultSwapAmountUpdateTrigger
|
||||
import com.tangem.features.swap.v2.impl.amount.SwapAmountReduceListener
|
||||
import com.tangem.features.swap.v2.impl.amount.SwapAmountReduceTrigger
|
||||
import com.tangem.features.swap.v2.impl.amount.SwapAmountUpdateListener
|
||||
import com.tangem.features.swap.v2.impl.amount.model.SwapAmountModel
|
||||
import dagger.Binds
|
||||
|
|
@ -32,4 +34,10 @@ internal interface SwapAmountModuleBinds {
|
|||
|
||||
@Binds
|
||||
fun provideSwapAmountUpdateListener(impl: DefaultSwapAmountUpdateTrigger): SwapAmountUpdateListener
|
||||
|
||||
@Binds
|
||||
fun provideSwapAmountReduceTrigger(impl: DefaultSwapAmountUpdateTrigger): SwapAmountReduceTrigger
|
||||
|
||||
@Binds
|
||||
fun provideSwapAmountReduceListener(impl: DefaultSwapAmountUpdateTrigger): SwapAmountReduceListener
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.express.models.ExpressError
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.models.SwapDirection
|
||||
import com.tangem.domain.swap.models.SwapDirection.Companion.withSwapDirection
|
||||
import com.tangem.domain.swap.models.SwapQuoteModel
|
||||
import com.tangem.domain.swap.usecase.GetSwapPairsUseCase
|
||||
import com.tangem.domain.swap.usecase.GetSwapQuoteUseCase
|
||||
|
|
@ -26,10 +27,12 @@ import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
|
|||
import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.transaction.usecase.GetAllowanceUseCase
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
|
||||
import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkListener
|
||||
import com.tangem.features.swap.v2.impl.R
|
||||
import com.tangem.features.swap.v2.impl.amount.SwapAmountBlockComponent.SwapChooseProviderConfig
|
||||
import com.tangem.features.swap.v2.impl.amount.SwapAmountComponentParams
|
||||
import com.tangem.features.swap.v2.impl.amount.SwapAmountReduceListener
|
||||
import com.tangem.features.swap.v2.impl.amount.SwapAmountUpdateListener
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
|
||||
|
|
@ -40,6 +43,7 @@ import com.tangem.features.swap.v2.impl.chooseprovider.SwapChooseProviderCompone
|
|||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.transformer.update
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
|
@ -65,6 +69,8 @@ internal class SwapAmountModel @Inject constructor(
|
|||
private val appRouter: AppRouter,
|
||||
private val swapAlertFactory: SwapAlertFactory,
|
||||
private val swapAmountUpdateListener: SwapAmountUpdateListener,
|
||||
private val swapAmountReduceListener: SwapAmountReduceListener,
|
||||
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
|
||||
) : Model(), SwapAmountClickIntents, SwapChooseProviderComponent.ModelCallback {
|
||||
|
||||
private val params: SwapAmountComponentParams = paramsContainer.require()
|
||||
|
|
@ -92,6 +98,9 @@ internal class SwapAmountModel @Inject constructor(
|
|||
subscribeOnCryptoCurrencyStatusFlow()
|
||||
subscribeOnAmountUpdateTriggerUpdates()
|
||||
observeChooseSelectToken()
|
||||
subscribeOnAmountReduceToTriggerUpdates()
|
||||
subscribeOnAmountReduceByTriggerUpdates()
|
||||
subscribeOnAmountIgnoreReduceTriggerUpdates()
|
||||
}
|
||||
|
||||
fun updateState(amountUM: SwapAmountUM) {
|
||||
|
|
@ -245,6 +254,44 @@ internal class SwapAmountModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnAmountReduceToTriggerUpdates() {
|
||||
swapAmountReduceListener.reduceToTriggerFlow
|
||||
.onEach { reduceTo ->
|
||||
uiState.update(
|
||||
SwapAmountReduceToTransformer(
|
||||
primaryMinimumAmountBoundary = primaryMinimumAmountBoundary,
|
||||
secondaryMinimumAmountBoundary = secondaryMinimumAmountBoundary,
|
||||
reduceToValue = reduceTo,
|
||||
),
|
||||
)
|
||||
feeSelectorReloadTrigger.triggerLoadingState()
|
||||
loadQuotes(reloadFees = true)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnAmountReduceByTriggerUpdates() {
|
||||
swapAmountReduceListener.reduceByTriggerFlow
|
||||
.onEach { reduceBy ->
|
||||
uiState.update(
|
||||
SwapAmountReduceByTransformer(
|
||||
primaryMinimumAmountBoundary = primaryMinimumAmountBoundary,
|
||||
secondaryMinimumAmountBoundary = secondaryMinimumAmountBoundary,
|
||||
reduceByData = reduceBy,
|
||||
),
|
||||
)
|
||||
feeSelectorReloadTrigger.triggerLoadingState()
|
||||
loadQuotes(reloadFees = true)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnAmountIgnoreReduceTriggerUpdates() {
|
||||
swapAmountReduceListener.ignoreReduceTriggerFlow
|
||||
.onEach { uiState.update(SwapAmountIgnoreReduceTransformer) }
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun observeChooseSelectToken() {
|
||||
swapChooseTokenNetworkListener.swapChooseTokenNetworkResultFlow
|
||||
.onEach { currency ->
|
||||
|
|
@ -345,18 +392,14 @@ internal class SwapAmountModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadQuotes() {
|
||||
private fun loadQuotes(reloadFees: Boolean = false) {
|
||||
val state = uiState.value as? SwapAmountUM.Content ?: return
|
||||
if (state.secondaryCryptoCurrencyStatus == null) return
|
||||
|
||||
val (fromCryptoCurrency, toCryptoCurrency) = when (state.swapDirection) {
|
||||
SwapDirection.Direct -> {
|
||||
state.primaryCryptoCurrencyStatus.currency to state.secondaryCryptoCurrencyStatus.currency
|
||||
}
|
||||
SwapDirection.Reverse -> {
|
||||
state.secondaryCryptoCurrencyStatus.currency to state.primaryCryptoCurrencyStatus.currency
|
||||
}
|
||||
}
|
||||
val (fromCryptoCurrency, toCryptoCurrency) = state.swapDirection.withSwapDirection(
|
||||
onDirect = { state.primaryCryptoCurrencyStatus.currency to state.secondaryCryptoCurrencyStatus.currency },
|
||||
onReverse = { state.secondaryCryptoCurrencyStatus.currency to state.primaryCryptoCurrencyStatus.currency },
|
||||
)
|
||||
|
||||
val fromAmount = when (state.swapDirection) {
|
||||
SwapDirection.Direct -> state.primaryAmount.amountField
|
||||
|
|
@ -420,6 +463,10 @@ internal class SwapAmountModel @Inject constructor(
|
|||
secondaryMinimumAmountBoundary = secondaryMinimumAmountBoundary,
|
||||
),
|
||||
)
|
||||
|
||||
if (reloadFees) {
|
||||
feeSelectorReloadTrigger.triggerUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.features.swap.v2.impl.amount.model.transformers
|
||||
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountIgnoreReduceTransformer
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
||||
import com.tangem.features.swap.v2.impl.amount.model.SwapAmountQuoteUtils.updateAmount
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal object SwapAmountIgnoreReduceTransformer : Transformer<SwapAmountUM> {
|
||||
override fun transform(prevState: SwapAmountUM): SwapAmountUM {
|
||||
if (prevState !is SwapAmountUM.Content) return prevState
|
||||
|
||||
return prevState
|
||||
.copy(selectedQuote = SwapQuoteUM.Loading)
|
||||
.updateAmount(
|
||||
onPrimaryAmount = { _ ->
|
||||
copy(
|
||||
amountField = AmountIgnoreReduceTransformer.transform(prevState.primaryAmount.amountField),
|
||||
)
|
||||
},
|
||||
onSecondaryAmount = { _ ->
|
||||
copy(
|
||||
amountField = AmountIgnoreReduceTransformer.transform(prevState.secondaryAmount.amountField),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.features.swap.v2.impl.amount.model.transformers
|
||||
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
||||
import com.tangem.features.swap.v2.impl.amount.model.SwapAmountQuoteUtils.updateAmount
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SwapAmountReduceByTransformer(
|
||||
private val primaryMinimumAmountBoundary: EnterAmountBoundary?,
|
||||
private val secondaryMinimumAmountBoundary: EnterAmountBoundary?,
|
||||
private val reduceByData: AmountReduceByTransformer.ReduceByData,
|
||||
) : Transformer<SwapAmountUM> {
|
||||
override fun transform(prevState: SwapAmountUM): SwapAmountUM {
|
||||
if (prevState !is SwapAmountUM.Content) return prevState
|
||||
|
||||
return prevState
|
||||
.copy(selectedQuote = SwapQuoteUM.Loading)
|
||||
.updateAmount(
|
||||
onPrimaryAmount = { primaryStatus ->
|
||||
copy(
|
||||
amountField = AmountReduceByTransformer(
|
||||
cryptoCurrencyStatus = primaryStatus,
|
||||
minimumTransactionAmount = primaryMinimumAmountBoundary,
|
||||
value = reduceByData,
|
||||
).transform(prevState.primaryAmount.amountField),
|
||||
)
|
||||
},
|
||||
onSecondaryAmount = { secondaryStatus ->
|
||||
copy(
|
||||
amountField = AmountReduceByTransformer(
|
||||
cryptoCurrencyStatus = secondaryStatus,
|
||||
minimumTransactionAmount = secondaryMinimumAmountBoundary,
|
||||
value = reduceByData,
|
||||
).transform(prevState.secondaryAmount.amountField),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tangem.features.swap.v2.impl.amount.model.transformers
|
||||
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceToTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
||||
import com.tangem.features.swap.v2.impl.amount.model.SwapAmountQuoteUtils.updateAmount
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class SwapAmountReduceToTransformer(
|
||||
private val primaryMinimumAmountBoundary: EnterAmountBoundary?,
|
||||
private val secondaryMinimumAmountBoundary: EnterAmountBoundary?,
|
||||
private val reduceToValue: BigDecimal,
|
||||
) : Transformer<SwapAmountUM> {
|
||||
override fun transform(prevState: SwapAmountUM): SwapAmountUM {
|
||||
if (prevState !is SwapAmountUM.Content) return prevState
|
||||
|
||||
return prevState
|
||||
.copy(selectedQuote = SwapQuoteUM.Loading)
|
||||
.updateAmount(
|
||||
onPrimaryAmount = { primaryStatus ->
|
||||
copy(
|
||||
amountField = AmountReduceToTransformer(
|
||||
cryptoCurrencyStatus = primaryStatus,
|
||||
minimumTransactionAmount = primaryMinimumAmountBoundary,
|
||||
value = reduceToValue,
|
||||
).transform(prevState.primaryAmount.amountField),
|
||||
)
|
||||
},
|
||||
onSecondaryAmount = { secondaryStatus ->
|
||||
copy(
|
||||
amountField = AmountReduceToTransformer(
|
||||
cryptoCurrencyStatus = secondaryStatus,
|
||||
minimumTransactionAmount = secondaryMinimumAmountBoundary,
|
||||
value = reduceToValue,
|
||||
).transform(prevState.secondaryAmount.amountField),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import arrow.core.Either
|
|||
import arrow.core.getOrElse
|
||||
import arrow.core.left
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButton
|
||||
import com.tangem.common.ui.navigationButtons.NavigationUM
|
||||
|
|
@ -30,6 +31,7 @@ import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorRelo
|
|||
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateListener
|
||||
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateTrigger
|
||||
import com.tangem.features.swap.v2.impl.R
|
||||
import com.tangem.features.swap.v2.impl.amount.SwapAmountReduceTrigger
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
||||
import com.tangem.features.swap.v2.impl.common.SwapUtils.INCREASE_GAS_LIMIT_FOR_CEX
|
||||
import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM
|
||||
|
|
@ -48,6 +50,7 @@ import com.tangem.utils.extensions.orZero
|
|||
import jakarta.inject.Inject
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
import com.tangem.utils.transformer.update as transformerUpdate
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -63,6 +66,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
|||
private val swapNotificationsUpdateTrigger: SwapNotificationsUpdateTrigger,
|
||||
private val sendNotificationsUpdateListener: SendNotificationsUpdateListener,
|
||||
private val swapNotificationsUpdateListener: SwapNotificationsUpdateListener,
|
||||
private val swapAmountReduceTrigger: SwapAmountReduceTrigger,
|
||||
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
|
||||
swapTransactionSenderFactory: SwapTransactionSender.Factory,
|
||||
paramsContainer: ParamsContainer,
|
||||
|
|
@ -152,6 +156,29 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onAmountIgnore() {
|
||||
modelScope.launch {
|
||||
swapAmountReduceTrigger.triggerIgnoreReduce()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAmountReduceBy(reduceBy: BigDecimal, reduceByDiff: BigDecimal) {
|
||||
modelScope.launch {
|
||||
swapAmountReduceTrigger.triggerReduceBy(
|
||||
reduceBy = AmountReduceByTransformer.ReduceByData(
|
||||
reduceAmountBy = reduceBy,
|
||||
reduceAmountByDiff = reduceByDiff,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAmountReduceTo(reduceTo: BigDecimal) {
|
||||
modelScope.launch {
|
||||
swapAmountReduceTrigger.triggerReduceTo(reduceTo = reduceTo)
|
||||
}
|
||||
}
|
||||
|
||||
fun showEditAmount() {
|
||||
router.push(SendWithSwapRoute.Amount(isEditMode = true))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.features.swap.v2.impl.sendviaswap.confirm.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -41,20 +40,15 @@ internal fun SendWithSwapConfirmContent(
|
|||
|
||||
Column(modifier = modifier) {
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier.padding(horizontal = 12.dp),
|
||||
) {
|
||||
item(key = "SendWithSwapAmountBlock") {
|
||||
amountBlockComponent.Content(Modifier)
|
||||
}
|
||||
item(key = "SendWithSwapDestinationBlock") {
|
||||
sendDestinationBlockComponent.Content(Modifier)
|
||||
}
|
||||
item(key = "SendWithSwapFeeBLock") {
|
||||
Box(
|
||||
modifier = Modifier.clip(RoundedCornerShape(16.dp)),
|
||||
item(key = "SendWithSwapBlocks") {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
feeSelectorBlockComponent.Content(Modifier)
|
||||
amountBlockComponent.Content(Modifier)
|
||||
sendDestinationBlockComponent.Content(Modifier)
|
||||
feeSelectorBlockComponent.Content(Modifier.clip(RoundedCornerShape(16.dp)))
|
||||
}
|
||||
}
|
||||
if (confirmUM != null) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue