Updated on 2026-08-14
This commit is contained in:
parent
c62f464b86
commit
e047c491cf
11 changed files with 158 additions and 20 deletions
|
|
@ -12,11 +12,15 @@ import com.arkivanov.decompose.router.slot.activate
|
|||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.arkivanov.essenty.lifecycle.subscribe
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionState
|
||||
import com.tangem.common.ui.swapStoriesScreen.SwapStoriesScreen
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.feature.swap.component.SwapFeeSelectorBlockComponent
|
||||
|
|
@ -26,6 +30,7 @@ import com.tangem.feature.swap.router.SwapNavScreen
|
|||
import com.tangem.feature.swap.ui.SwapScreen
|
||||
import com.tangem.feature.swap.ui.SwapSelectTokenScreen
|
||||
import com.tangem.feature.swap.ui.SwapSuccessScreen
|
||||
import com.tangem.features.approval.api.GiveApprovalComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioComponent
|
||||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||
|
|
@ -43,6 +48,7 @@ internal class DefaultSwapComponent @AssistedInject constructor(
|
|||
private val swapFeeSelectorBlockComponentFactory: SwapFeeSelectorBlockComponent.Factory,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
|
||||
private val giveApprovalComponentFactory: GiveApprovalComponent.Factory,
|
||||
) : SwapComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: SwapModel = getOrCreateModel(params)
|
||||
|
|
@ -55,6 +61,21 @@ internal class DefaultSwapComponent @AssistedInject constructor(
|
|||
childFactory = { configuration, context -> bottomSheetChild(context) },
|
||||
)
|
||||
|
||||
private val approvalSlot = childSlot(
|
||||
key = APPROVAL_SLOT_KEY,
|
||||
source = model.approvalSlotNavigation,
|
||||
serializer = null,
|
||||
handleBackButton = true,
|
||||
childFactory = { _, factoryContext ->
|
||||
val approvalParams = getApprovalParams()
|
||||
?: error("Approval params are not available")
|
||||
giveApprovalComponentFactory.create(
|
||||
context = childByContext(factoryContext),
|
||||
params = approvalParams,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
init {
|
||||
lifecycle.subscribe(
|
||||
onStart = model::onStart,
|
||||
|
|
@ -191,6 +212,9 @@ internal class DefaultSwapComponent @AssistedInject constructor(
|
|||
}
|
||||
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
|
||||
val approvalSlotState by approvalSlot.subscribeAsState()
|
||||
approvalSlotState.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
||||
@Suppress("UnsafeCallOnNullableType")
|
||||
|
|
@ -205,6 +229,27 @@ internal class DefaultSwapComponent @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun getApprovalParams(): GiveApprovalComponent.Params? {
|
||||
val permissionState = model.uiState.permissionState as? GiveTxPermissionState.ReadyForRequest
|
||||
?: return null
|
||||
val fromCryptoCurrency = model.dataState.fromCryptoCurrency ?: return null
|
||||
val feeCryptoCurrency = model.dataState.feePaidCryptoCurrency ?: return null
|
||||
val providerName = model.dataState.selectedProvider?.name.orEmpty()
|
||||
|
||||
return GiveApprovalComponent.Params(
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrencyStatus = fromCryptoCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrency,
|
||||
amount = permissionState.amount,
|
||||
spenderAddress = requireNotNull(model.dataState.approveDataModel).spenderAddress,
|
||||
subtitle = resourceReference(
|
||||
id = R.string.give_permission_swap_subtitle,
|
||||
formatArgs = wrappedList(providerName, permissionState.currency),
|
||||
),
|
||||
callback = model.approvalCallback,
|
||||
)
|
||||
}
|
||||
|
||||
private fun toBigDecimalOrZero(bigDecimalString: String?): BigDecimal {
|
||||
return bigDecimalString?.replace(",", ".")?.toBigDecimalOrNull() ?: BigDecimal.ZERO
|
||||
}
|
||||
|
|
@ -217,5 +262,6 @@ internal class DefaultSwapComponent @AssistedInject constructor(
|
|||
private companion object {
|
||||
const val BOTTOM_SHEET_SLOT_KEY = "bottomSheetSlot"
|
||||
const val FEE_SELECTOR_SLOT_KEY = "feeSelectorSlot"
|
||||
const val APPROVAL_SLOT_KEY = "approvalSlot"
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionState.InProgress.getApproveTypeOrNull
|
||||
import com.tangem.features.approval.api.GiveApprovalComponent
|
||||
import com.tangem.features.approval.api.GiveApprovalFeatureToggles
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
|
|
@ -31,9 +33,9 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.toWrappedList
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.core.ui.utils.InputNumberFormatter
|
||||
|
|
@ -118,6 +120,8 @@ import com.tangem.utils.Provider
|
|||
import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP
|
||||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
|
|
@ -173,6 +177,7 @@ internal class SwapModel @Inject constructor(
|
|||
private val appsFlyerStore: AppsFlyerStore,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
private val messageSender: UiMessageSender,
|
||||
giveApprovalFeatureToggles: GiveApprovalFeatureToggles,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<SwapComponent.Params>()
|
||||
|
|
@ -301,6 +306,33 @@ internal class SwapModel @Inject constructor(
|
|||
get() = swapRouter.currentScreen
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<AddToPortfolioRoute> = SlotNavigation()
|
||||
val approvalSlotNavigation = SlotNavigation<Unit>()
|
||||
private val shouldUseGaslessApproval: Boolean = giveApprovalFeatureToggles.isGaslessApprovalEnabled
|
||||
|
||||
val approvalCallback = object : GiveApprovalComponent.Callback {
|
||||
override fun onApproveClick() {
|
||||
sendPermissionApproveClickedEvent()
|
||||
}
|
||||
|
||||
override fun onApproveDone() {
|
||||
approvalSlotNavigation.dismiss()
|
||||
updateWalletBalance()
|
||||
uiState = stateBuilder.loadingPermissionState(uiState)
|
||||
startLoadingQuotesFromLastState(isSilent = true)
|
||||
}
|
||||
|
||||
override fun onApproveFailed() {
|
||||
approvalSlotNavigation.dismiss()
|
||||
showAlert()
|
||||
}
|
||||
|
||||
override fun onCancelClick() {
|
||||
approvalSlotNavigation.dismiss()
|
||||
startLoadingQuotesFromLastState(isSilent = true)
|
||||
analyticsEventHandler.send(SwapEvents.ButtonPermissionCancelClicked())
|
||||
}
|
||||
}
|
||||
|
||||
val addToPortfolioCallback = object : AddToPortfolioComponent.Callback {
|
||||
override fun onDismiss() = bottomSheetNavigation.dismiss()
|
||||
|
||||
|
|
@ -1224,7 +1256,7 @@ internal class SwapModel @Inject constructor(
|
|||
fromTokenStatus = fromCryptoCurrency,
|
||||
approveType = approveType,
|
||||
txFee = feeForPermission,
|
||||
spenderAddress = approveDataModel.spenderAddress,
|
||||
spenderAddress = requireNotNull(dataState.approveDataModel).spenderAddress,
|
||||
),
|
||||
)
|
||||
}.onSuccess { swapTransactionState ->
|
||||
|
|
@ -1785,10 +1817,14 @@ internal class SwapModel @Inject constructor(
|
|||
openPermissionBottomSheet = {
|
||||
singleTaskScheduler.cancelTask()
|
||||
sendGivePermissionClickedEvent()
|
||||
uiState = stateBuilder.showPermissionBottomSheet(uiState) {
|
||||
startLoadingQuotesFromLastState(isSilent = true)
|
||||
analyticsEventHandler.send(SwapEvents.ButtonPermissionCancelClicked())
|
||||
uiState = stateBuilder.dismissBottomSheet(uiState)
|
||||
if (shouldUseGaslessApproval) {
|
||||
approvalSlotNavigation.activate(Unit)
|
||||
} else {
|
||||
uiState = stateBuilder.showPermissionBottomSheet(uiState) {
|
||||
startLoadingQuotesFromLastState(isSilent = true)
|
||||
analyticsEventHandler.send(SwapEvents.ButtonPermissionCancelClicked())
|
||||
uiState = stateBuilder.dismissBottomSheet(uiState)
|
||||
}
|
||||
}
|
||||
},
|
||||
onAmountSelected = { onAmountSelected(it) },
|
||||
|
|
|
|||
|
|
@ -711,6 +711,9 @@ internal class StateBuilder(
|
|||
fun createSilentLoadState(uiState: SwapStateHolder): SwapStateHolder {
|
||||
return uiState.copy(
|
||||
changeCardsButtonState = ChangeCardsButtonState.UPDATE_IN_PROGRESS,
|
||||
notifications = uiState.notifications
|
||||
.filterNot { it is SwapNotificationUM.Info.PermissionNeeded }
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue