Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-23 15:05:14 +05:00
commit c813e330c2
21 changed files with 464 additions and 119 deletions

View file

@ -31,6 +31,7 @@ interface SendNotificationsComponent {
val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
val appCurrency: AppCurrency,
val notificationData: NotificationData,
val callback: ModelCallback,
) {
data class NotificationData(
val destinationAddress: String,
@ -44,4 +45,14 @@ interface SendNotificationsComponent {
}
interface Factory : ComponentFactory<Params, SendNotificationsComponent>
interface ModelCallback {
fun onFeeReload()
fun onAmountReduceTo(reduceTo: BigDecimal) {}
fun onAmountReduceBy(reduceBy: BigDecimal, reduceByDiff: BigDecimal) {}
fun onAmountIgnore() {}
}
}

View file

@ -1,13 +0,0 @@
package com.tangem.features.send.v2.api.feeselector
import kotlinx.coroutines.flow.Flow
data class FeeSelectorReloadData(val removeSuggestedFee: Boolean = false)
interface FeeSelectorReloadTrigger {
suspend fun triggerUpdate(data: FeeSelectorReloadData = FeeSelectorReloadData())
}
interface FeeSelectorReloadListener {
val reloadTriggerFlow: Flow<FeeSelectorReloadData>
}

View file

@ -0,0 +1,22 @@
package com.tangem.features.send.v2.api.subcomponents.feeSelector
import kotlinx.coroutines.flow.Flow
interface FeeSelectorCheckReloadTrigger {
/** Trigger return callback with check result */
suspend fun callbackCheckResult(isSuccess: Boolean)
/** Trigger fee check reload */
suspend fun triggerCheckUpdate()
}
interface FeeSelectorCheckReloadListener {
/**
* Flow triggers fee check reload before transaction.
* Usually after significant time after fee was updated last time
*/
val checkReloadTriggerFlow: Flow<Unit>
/** Flow return result of fee check update */
val checkReloadResultFlow: Flow<Boolean>
}

View file

@ -0,0 +1,15 @@
package com.tangem.features.send.v2.api.subcomponents.feeSelector
import com.tangem.features.send.v2.api.subcomponents.feeSelector.entity.FeeSelectorData
import kotlinx.coroutines.flow.Flow
interface FeeSelectorReloadTrigger {
/** Trigger fee update */
suspend fun triggerUpdate(feeData: FeeSelectorData = FeeSelectorData())
}
interface FeeSelectorReloadListener {
/** Flow triggers fee reload */
val reloadTriggerFlow: Flow<FeeSelectorData>
}

View file

@ -0,0 +1,5 @@
package com.tangem.features.send.v2.api.subcomponents.feeSelector.entity
data class FeeSelectorData(
val removeSuggestedFee: Boolean = false,
)

View file

@ -60,7 +60,7 @@ internal class DefaultFeeSelectorBlockComponent @AssistedInject constructor(
init {
model.uiState
.onEach(onResult)
.onEach { onResult(it) }
.launchIn(componentScope)
}

View file

@ -1,20 +0,0 @@
package com.tangem.features.send.v2.feeselector
import com.tangem.features.send.v2.api.feeselector.FeeSelectorReloadData
import com.tangem.features.send.v2.api.feeselector.FeeSelectorReloadListener
import com.tangem.features.send.v2.api.feeselector.FeeSelectorReloadTrigger
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
internal class DefaultFeeSelectorReload @Inject constructor() : FeeSelectorReloadListener, FeeSelectorReloadTrigger {
private val _reloadTriggerFlow = MutableSharedFlow<FeeSelectorReloadData>()
override val reloadTriggerFlow: Flow<FeeSelectorReloadData> = _reloadTriggerFlow.asSharedFlow()
override suspend fun triggerUpdate(data: FeeSelectorReloadData) {
_reloadTriggerFlow.emit(data)
}
}

View file

@ -0,0 +1,40 @@
package com.tangem.features.send.v2.feeselector
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorCheckReloadListener
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorCheckReloadTrigger
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadListener
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.v2.api.subcomponents.feeSelector.entity.FeeSelectorData
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
internal class DefaultFeeSelectorReloadTrigger @Inject constructor() :
FeeSelectorReloadTrigger,
FeeSelectorReloadListener,
FeeSelectorCheckReloadTrigger,
FeeSelectorCheckReloadListener {
override val reloadTriggerFlow: SharedFlow<FeeSelectorData>
field = MutableSharedFlow()
override val checkReloadTriggerFlow: SharedFlow<Unit>
field = MutableSharedFlow()
override val checkReloadResultFlow: SharedFlow<Boolean>
field = MutableSharedFlow()
override suspend fun triggerUpdate(feeData: FeeSelectorData) {
reloadTriggerFlow.emit(feeData)
}
override suspend fun triggerCheckUpdate() {
checkReloadTriggerFlow.emit(Unit)
}
override suspend fun callbackCheckResult(isSuccess: Boolean) {
checkReloadResultFlow.emit(isSuccess)
}
}

View file

@ -2,11 +2,13 @@ package com.tangem.features.send.v2.feeselector.di
import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
import com.tangem.features.send.v2.api.FeeSelectorComponent
import com.tangem.features.send.v2.api.feeselector.FeeSelectorReloadListener
import com.tangem.features.send.v2.api.feeselector.FeeSelectorReloadTrigger
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorCheckReloadListener
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorCheckReloadTrigger
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadListener
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.v2.feeselector.DefaultFeeSelectorBlockComponent
import com.tangem.features.send.v2.feeselector.DefaultFeeSelectorComponent
import com.tangem.features.send.v2.feeselector.DefaultFeeSelectorReload
import com.tangem.features.send.v2.feeselector.DefaultFeeSelectorReloadTrigger
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@ -27,9 +29,17 @@ internal interface FeeSelectorFeatureModule {
@Binds
@Singleton
fun provideFeeSelectorReloadTrigger(impl: DefaultFeeSelectorReload): FeeSelectorReloadTrigger
fun provideFeeSelectorReloadTrigger(impl: DefaultFeeSelectorReloadTrigger): FeeSelectorReloadTrigger
@Binds
@Singleton
fun provideFeeSelectorReloadListener(impl: DefaultFeeSelectorReload): FeeSelectorReloadListener
fun provideFeeSelectorReloadListener(impl: DefaultFeeSelectorReloadTrigger): FeeSelectorReloadListener
@Binds
@Singleton
fun provideFeeSelectorCheckReloadTrigger(impl: DefaultFeeSelectorReloadTrigger): FeeSelectorCheckReloadTrigger
@Binds
@Singleton
fun provideFeeSelectorCheckReloadListener(impl: DefaultFeeSelectorReloadTrigger): FeeSelectorCheckReloadListener
}

View file

@ -0,0 +1,136 @@
package com.tangem.features.send.v2.feeselector.model
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.features.send.v2.api.entity.FeeItem
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils
import com.tangem.features.send.v2.impl.R
import java.math.BigDecimal
import javax.inject.Inject
@ModelScoped
internal class FeeSelectorAlertFactory @Inject constructor(
private val messageSender: UiMessageSender,
) {
fun checkAndShowAlerts(feeSelectorUM: FeeSelectorUM.Content, onConfirmClick: () -> Unit) {
val showFeeTooLow = checkAndShowFeeTooLow(feeSelectorUM, onConfirmClick)
val showFeeTooHigh = checkAndShowFeeTooHigh(feeSelectorUM, onConfirmClick)
if (!showFeeTooLow && !showFeeTooHigh) {
onConfirmClick()
}
}
/**
* Check if custom fee is too low
*/
private fun checkAndShowFeeTooLow(feeSelectorUM: FeeSelectorUM.Content, onConfirmClick: () -> Unit): Boolean {
val isFeeTooLow = FeeCalculationUtils.checkIfCustomFeeTooLow(feeSelectorUM)
if (isFeeTooLow) {
messageSender.send(
DialogMessage(
message = resourceReference(id = R.string.send_alert_fee_too_low_text),
dismissOnFirstAction = true,
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.common_continue),
onClick = onConfirmClick,
)
},
secondActionBuilder = { cancelAction() },
),
)
}
return isFeeTooLow
}
/**
* Check if custom fee is too high
*/
private fun checkAndShowFeeTooHigh(feeSelectorUM: FeeSelectorUM.Content, onConfirmClick: () -> Unit): Boolean {
val (isFeeTooHigh, diff) = FeeCalculationUtils.checkIfCustomFeeTooHigh(feeSelectorUM)
if (isFeeTooHigh) {
messageSender.send(
DialogMessage(
message = resourceReference(
id = R.string.send_alert_fee_too_high_text,
formatArgs = wrappedList(diff),
),
dismissOnFirstAction = true,
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.common_continue),
onClick = onConfirmClick,
)
},
secondActionBuilder = { cancelAction() },
),
)
}
return isFeeTooHigh
}
fun getFeeUpdatedAlert(
newFee: TransactionFee,
feeSelectorUM: FeeSelectorUM,
proceedAction: () -> Unit,
stopAction: () -> Unit,
) {
if (feeSelectorUM !is FeeSelectorUM.Content) return
val newFee = when (newFee) {
is TransactionFee.Single -> newFee.normal
is TransactionFee.Choosable -> {
when (feeSelectorUM.selectedFeeItem) {
is FeeItem.Suggested -> feeSelectorUM.selectedFeeItem.fee
is FeeItem.Slow -> newFee.minimum
is FeeItem.Market -> newFee.normal
is FeeItem.Fast -> newFee.priority
is FeeItem.Custom -> return
}
}
}
val newFeeValue = newFee.amount.value ?: BigDecimal.ZERO
val oldFeeValue = feeSelectorUM.selectedFeeItem.fee.amount.value ?: BigDecimal.ZERO
if (newFeeValue > oldFeeValue) {
messageSender.send(
DialogMessage(
message = resourceReference(id = R.string.send_notification_high_fee_title),
dismissOnFirstAction = true,
firstActionBuilder = {
okAction { proceedAction(); onDismissRequest() }
},
secondActionBuilder = {
cancelAction { stopAction(); onDismissRequest() }
},
),
)
} else {
proceedAction()
}
}
fun getFeeUnreachableErrorState(onFeeReload: () -> Unit) {
messageSender.send(
DialogMessage(
title = resourceReference(R.string.send_fee_unreachable_error_title),
message = resourceReference(R.string.send_fee_unreachable_error_text),
dismissOnFirstAction = true,
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.warning_button_refresh),
onClick = onFeeReload,
)
},
),
)
}
}

View file

@ -14,8 +14,10 @@ import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback
import com.tangem.features.send.v2.api.entity.FeeItem
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.feeselector.FeeSelectorReloadListener
import com.tangem.features.send.v2.api.params.FeeSelectorParams
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorCheckReloadListener
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorCheckReloadTrigger
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadListener
import com.tangem.features.send.v2.feeselector.model.transformers.*
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.transformer.update
@ -26,14 +28,18 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("LongParameterList")
@Stable
@ModelScoped
internal class FeeSelectorModel @Inject constructor(
paramsContainer: ParamsContainer,
private val feeSelectorReloadListener: FeeSelectorReloadListener,
private val isFeeApproximateUseCase: IsFeeApproximateUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
override val dispatchers: CoroutineDispatcherProvider,
private val feeSelectorReloadListener: FeeSelectorReloadListener,
private val feeSelectorCheckReloadListener: FeeSelectorCheckReloadListener,
private val feeSelectorCheckReloadTrigger: FeeSelectorCheckReloadTrigger,
private val feeSelectorAlertFactory: FeeSelectorAlertFactory,
) : Model(), FeeSelectorIntents, FeeSelectorModelCallback {
private val params = paramsContainer.require<FeeSelectorParams>()
@ -46,25 +52,15 @@ internal class FeeSelectorModel @Inject constructor(
init {
initAppCurrency()
subscribeOnFeeReloadTriggerUpdates()
subscribeOnFeeCheckReloadTriggerUpdates()
loadFee()
listenReloadTrigger()
}
fun updateState(feeSelectorUM: FeeSelectorUM) {
uiState.value = feeSelectorUM
}
private fun listenReloadTrigger() {
feeSelectorReloadListener.reloadTriggerFlow
.onEach { data ->
if (data.removeSuggestedFee) {
uiState.update(FeeSelectorRemoveSuggestedTransformer)
}
loadFee()
}
.launchIn(modelScope)
}
private fun initAppCurrency() {
modelScope.launch {
appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }
@ -127,4 +123,48 @@ internal class FeeSelectorModel @Inject constructor(
uiState.value = feeSelectorUM
feeSelectorBottomSheet.dismiss()
}
private fun subscribeOnFeeReloadTriggerUpdates() {
feeSelectorReloadListener.reloadTriggerFlow
.onEach { data ->
if (data.removeSuggestedFee) {
uiState.update(FeeSelectorRemoveSuggestedTransformer)
}
loadFee()
}
.launchIn(modelScope)
}
private fun subscribeOnFeeCheckReloadTriggerUpdates() {
feeSelectorCheckReloadListener.checkReloadTriggerFlow
.onEach { checkLoadFee() }
.launchIn(modelScope)
}
private fun checkLoadFee() {
modelScope.launch {
params.onLoadFee().fold(
ifRight = {
feeSelectorAlertFactory.getFeeUpdatedAlert(
newFee = it,
feeSelectorUM = uiState.value,
proceedAction = {
modelScope.launch {
feeSelectorCheckReloadTrigger.callbackCheckResult(true)
}
},
stopAction = {
modelScope.launch {
feeSelectorCheckReloadTrigger.callbackCheckResult(false)
}
},
)
},
ifLeft = { feeError ->
feeSelectorCheckReloadTrigger.callbackCheckResult(false)
feeSelectorAlertFactory.getFeeUnreachableErrorState(::loadFee)
},
)
}
}
}

View file

@ -116,6 +116,7 @@ internal class SendConfirmComponent(
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
appCurrency = params.appCurrency,
callback = model,
notificationData = NotificationData(
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
memo = model.confirmData.enteredMemo,
@ -139,6 +140,7 @@ internal class SendConfirmComponent(
destinationBlockComponent.updateState(state.destinationUM)
amountBlockComponent.updateState(state.amountUM)
feeBlockComponent.updateState(state.feeUM)
feeSelectorBlockComponent.updateState(state.feeSelectorUM)
model.updateState(state)
}

View file

@ -6,6 +6,7 @@ import arrow.core.getOrElse
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.TransactionData
import com.tangem.common.routing.AppRouter
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
@ -34,12 +35,13 @@ import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback
import com.tangem.features.send.v2.api.entity.FeeNonce
import com.tangem.features.send.v2.api.params.FeeSelectorParams
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateListener
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateTrigger
import com.tangem.features.send.v2.common.CommonSendRoute
@ -51,14 +53,13 @@ import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.send.analytics.SendAnalyticHelper
import com.tangem.features.send.v2.send.confirm.SendConfirmComponent
import com.tangem.features.send.v2.send.confirm.model.transformers.SendConfirmInitialStateTransformer
import com.tangem.features.send.v2.send.confirm.model.transformers.SendConfirmSendingStateTransformer
import com.tangem.features.send.v2.send.confirm.model.transformers.SendConfirmSentStateTransformer
import com.tangem.features.send.v2.send.confirm.model.transformers.SendConfirmationNotificationsTransformer
import com.tangem.features.send.v2.send.confirm.model.transformers.SendConfirmationNotificationsTransformerV2
import com.tangem.features.send.v2.send.confirm.model.transformers.*
import com.tangem.features.send.v2.send.ui.state.SendUM
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceTrigger
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadListener
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadTrigger
import com.tangem.features.send.v2.subcomponents.fee.SendFeeData
import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadTrigger
import com.tangem.features.send.v2.subcomponents.fee.model.checkAndCalculateSubtractedAmount
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
@ -69,7 +70,9 @@ import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
import javax.inject.Inject
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
@Suppress("LongParameterList", "LargeClass")
@Stable
@ -98,8 +101,11 @@ internal class SendConfirmModel @Inject constructor(
private val sendAnalyticHelper: SendAnalyticHelper,
private val urlOpener: UrlOpener,
private val shareManager: ShareManager,
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
private val feeReloadTrigger: SendFeeReloadTrigger,
private val sendAmountReduceTrigger: SendAmountReduceTrigger,
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
) : Model(), SendConfirmClickIntents, FeeSelectorModelCallback {
) : Model(), SendConfirmClickIntents, FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback {
private val params: SendConfirmComponent.Params = paramsContainer.require()
@ -172,6 +178,44 @@ internal class SendConfirmModel @Inject constructor(
updateConfirmNotifications()
}
override fun onFeeReload() {
modelScope.launch {
if (uiState.value.isRedesignEnabled) {
feeSelectorReloadTrigger.triggerUpdate()
} else {
feeReloadTrigger.triggerUpdate(
feeData = SendFeeData(
amount = confirmData.enteredAmount,
destinationAddress = confirmData.enteredDestination,
),
)
}
}
}
override fun onAmountReduceTo(reduceTo: BigDecimal) {
modelScope.launch {
sendAmountReduceTrigger.triggerReduceTo(reduceTo)
}
}
override fun onAmountReduceBy(reduceBy: BigDecimal, reduceByDiff: BigDecimal) {
modelScope.launch {
sendAmountReduceTrigger.triggerReduceBy(
AmountReduceByTransformer.ReduceByData(
reduceAmountBy = reduceBy,
reduceAmountByDiff = reduceByDiff,
),
)
}
}
override fun onAmountIgnore() {
modelScope.launch {
sendAmountReduceTrigger.triggerIgnoreReduce()
}
}
override fun showEditDestination() {
modelScope.launch {
neverShowTapHelpUseCase()

View file

@ -91,6 +91,7 @@ internal class NFTSendConfirmComponent(
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
appCurrency = params.appCurrency,
callback = model,
notificationData = SendNotificationsComponent.Params.NotificationData(
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
memo = model.confirmData.enteredMemo,

View file

@ -28,6 +28,7 @@ import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.features.nft.entity.NFTSendSuccessTrigger
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateListener
@ -47,6 +48,8 @@ import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendCon
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadListener
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadTrigger
import com.tangem.features.send.v2.subcomponents.fee.SendFeeData
import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadTrigger
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -83,8 +86,9 @@ internal class NFTSendConfirmModel @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val nftSendAnalyticHelper: NFTSendAnalyticHelper,
private val nftSendSuccessTrigger: NFTSendSuccessTrigger,
private val sendFeeReloadTrigger: SendFeeReloadTrigger,
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
) : Model(), NFTSendConfirmClickIntents {
) : Model(), NFTSendConfirmClickIntents, SendNotificationsComponent.ModelCallback {
private val params: NFTSendConfirmComponent.Params = paramsContainer.require()
@ -141,6 +145,18 @@ internal class NFTSendConfirmModel @Inject constructor(
updateConfirmNotifications()
}
override fun onFeeReload() {
modelScope.launch {
sendFeeReloadTrigger.triggerUpdate(
// For now transfer one nft asset at a time
feeData = SendFeeData(
amount = BigDecimal.ZERO,
destinationAddress = confirmData.enteredDestination,
),
)
}
}
override fun showEditDestination() {
modelScope.launch {
neverShowTapHelpUseCase()

View file

@ -30,6 +30,7 @@ import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.SendFeatureToggles
import com.tangem.features.send.v2.api.entity.PredefinedValues
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener
@ -55,11 +56,12 @@ internal class SendAmountModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
private val sendAmountReduceListener: SendAmountReduceListener,
private val feeReloadTrigger: SendFeeReloadTrigger,
private val sendAmountUpdateListener: SendAmountUpdateListener,
private val analyticsEventHandler: AnalyticsEventHandler,
private val sendFeatureToggles: SendFeatureToggles,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val feeReloadTrigger: SendFeeReloadTrigger,
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val rampStateManager: RampStateManager,
) : Model(), SendAmountClickIntents {
@ -254,11 +256,14 @@ internal class SendAmountModel @Inject constructor(
value = reduceTo,
),
)
feeReloadTrigger.triggerUpdate(
feeData = SendFeeData(
amount = (uiState.value as? AmountState.Data)?.amountTextField?.cryptoAmount?.value,
),
)
val amountValue = (uiState.value as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
if (uiState.value.isRedesignEnabled) {
feeSelectorReloadTrigger.triggerUpdate()
} else {
feeReloadTrigger.triggerUpdate(
feeData = SendFeeData(amount = amountValue),
)
}
}
.launchIn(modelScope)
}
@ -273,11 +278,15 @@ internal class SendAmountModel @Inject constructor(
value = reduceByData,
),
)
feeReloadTrigger.triggerUpdate(
feeData = SendFeeData(
amount = (uiState.value as? AmountState.Data)?.amountTextField?.cryptoAmount?.value,
),
)
val amountValue = (uiState.value as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
if (uiState.value.isRedesignEnabled) {
feeSelectorReloadTrigger.triggerUpdate()
} else {
feeReloadTrigger.triggerUpdate(
feeData = SendFeeData(amount = amountValue),
)
}
}
.launchIn(modelScope)
}

View file

@ -6,7 +6,6 @@ import com.tangem.blockchain.common.transaction.Fee
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.R
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.common.ui.notifications.NotificationsFactory.addDustWarningNotification
import com.tangem.common.ui.notifications.NotificationsFactory.addExceedBalanceNotification
@ -39,9 +38,6 @@ import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateListener
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateTrigger
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceTrigger
import com.tangem.features.send.v2.subcomponents.fee.SendFeeData
import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadTrigger
import com.tangem.features.send.v2.subcomponents.fee.model.checkAndCalculateSubtractedAmount
import com.tangem.features.send.v2.subcomponents.fee.model.checkFeeCoverage
import com.tangem.features.send.v2.subcomponents.notifications.analytics.NotificationsAnalyticEvents
@ -73,8 +69,6 @@ internal class NotificationsModel @Inject constructor(
private val validateTransactionUseCase: ValidateTransactionUseCase,
private val getTronFeeNotificationShowCountUseCase: GetTronFeeNotificationShowCountUseCase,
private val incrementNotificationsShowCountUseCase: IncrementNotificationsShowCountUseCase,
private val sendFeeReloadTrigger: SendFeeReloadTrigger,
private val sendAmountReduceTrigger: SendAmountReduceTrigger,
private val notificationsUpdateTrigger: SendNotificationsUpdateTrigger,
private val notificationsUpdateListener: SendNotificationsUpdateListener,
private val analyticsEventHandler: AnalyticsEventHandler,
@ -132,16 +126,7 @@ internal class NotificationsModel @Inject constructor(
tokenStatus = cryptoCurrencyStatus,
coinStatus = feeCryptoCurrencyStatus,
feeError = notificationData.feeError,
onReload = {
modelScope.launch {
sendFeeReloadTrigger.triggerUpdate(
feeData = SendFeeData(
amount = notificationData.amountValue,
destinationAddress = notificationData.destinationAddress,
),
)
}
},
onReload = params.callback::onFeeReload,
onClick = ::showTokenDetails,
)
addDomainNotifications()
@ -274,9 +259,7 @@ internal class NotificationsModel @Inject constructor(
feeCurrencyStatus = feeCryptoCurrencyStatus,
feeValue = feeValue,
onReduceClick = { reduceTo, _ ->
modelScope.launch {
sendAmountReduceTrigger.triggerReduceTo(reduceTo)
}
params.callback.onAmountReduceTo(reduceTo)
},
)
addReserveAmountErrorNotification(
@ -321,14 +304,10 @@ internal class NotificationsModel @Inject constructor(
sendingAmount = sendingAmount,
cryptoCurrencyStatus = cryptoCurrencyStatus,
onReduceClick = { reduceBy, reduceByDiff, _ ->
modelScope.launch {
sendAmountReduceTrigger.triggerReduceBy(
ReduceByData(
reduceAmountBy = reduceBy,
reduceAmountByDiff = reduceByDiff,
),
)
}
params.callback.onAmountReduceBy(
reduceBy = reduceBy,
reduceByDiff = reduceByDiff,
)
},
)
addFeeCoverageNotification(
@ -344,9 +323,7 @@ internal class NotificationsModel @Inject constructor(
validationError = validationError,
cryptoCurrency = currency,
onReduceClick = { reduceTo, _ ->
modelScope.launch {
sendAmountReduceTrigger.triggerReduceTo(reduceTo)
}
params.callback.onAmountReduceTo(reduceTo)
},
)
addHighFeeWarningNotification(
@ -354,19 +331,13 @@ internal class NotificationsModel @Inject constructor(
cryptoCurrencyStatus = cryptoCurrencyStatus,
ignoreAmountReduce = notificationData.isIgnoreReduce,
onReduceClick = { reduceBy, reduceByDiff, _ ->
modelScope.launch {
sendAmountReduceTrigger.triggerReduceBy(
ReduceByData(
reduceAmountBy = reduceBy,
reduceAmountByDiff = reduceByDiff,
),
)
}
params.callback.onAmountReduceBy(
reduceBy = reduceBy,
reduceByDiff = reduceByDiff,
)
},
onCloseClick = {
modelScope.launch {
sendAmountReduceTrigger.triggerIgnoreReduce()
}
params.callback.onAmountIgnore()
},
)
}

View file

@ -1,8 +1,11 @@
package com.tangem.features.swap.v2.impl.amount
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData
import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import java.math.BigDecimal
import javax.inject.Inject
import javax.inject.Singleton
@ -14,15 +17,56 @@ interface SwapAmountUpdateListener {
val updateAmountTriggerFlow: Flow<String>
}
/**
* Trigger for reducing swap amount from another component
*/
interface SwapAmountReduceTrigger {
suspend fun triggerReduceBy(reduceBy: ReduceByData)
suspend fun triggerReduceTo(reduceTo: BigDecimal)
suspend fun triggerIgnoreReduce()
}
/**
* Trigger for reducing swap amount from another component
*/
interface SwapAmountReduceListener {
val reduceToTriggerFlow: Flow<BigDecimal>
val reduceByTriggerFlow: Flow<ReduceByData>
val ignoreReduceTriggerFlow: Flow<Unit>
}
@Singleton
internal class DefaultSwapAmountUpdateTrigger @Inject constructor() :
SwapAmountUpdateTrigger,
SwapAmountUpdateListener {
SwapAmountUpdateListener,
SwapAmountReduceTrigger,
SwapAmountReduceListener {
override val updateAmountTriggerFlow: Flow<String>
override val updateAmountTriggerFlow: SharedFlow<String>
field = MutableSharedFlow<String>()
override val reduceToTriggerFlow: SharedFlow<BigDecimal>
field = MutableSharedFlow<BigDecimal>()
override val reduceByTriggerFlow: SharedFlow<ReduceByData>
field = MutableSharedFlow<ReduceByData>()
override val ignoreReduceTriggerFlow: SharedFlow<Unit>
field = MutableSharedFlow<Unit>()
override suspend fun triggerUpdateAmount(amountValue: String) {
updateAmountTriggerFlow.emit(amountValue)
}
override suspend fun triggerReduceBy(reduceBy: ReduceByData) {
reduceByTriggerFlow.emit(reduceBy)
}
override suspend fun triggerReduceTo(reduceTo: BigDecimal) {
reduceToTriggerFlow.emit(reduceTo)
}
override suspend fun triggerIgnoreReduce() {
ignoreReduceTriggerFlow.emit(Unit)
}
}

View file

@ -99,6 +99,7 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor(
cryptoCurrencyStatus = model.primaryCurrencyStatus,
feeCryptoCurrencyStatus = model.primaryFeePaidCurrencyStatus,
appCurrency = params.appCurrency,
callback = model,
notificationData = SendNotificationsComponent.Params.NotificationData(
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
memo = null,

View file

@ -21,10 +21,12 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.EstimateFeeUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
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
@ -61,9 +63,10 @@ internal class SendWithSwapConfirmModel @Inject constructor(
private val swapNotificationsUpdateTrigger: SwapNotificationsUpdateTrigger,
private val sendNotificationsUpdateListener: SendNotificationsUpdateListener,
private val swapNotificationsUpdateListener: SwapNotificationsUpdateListener,
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
swapTransactionSenderFactory: SwapTransactionSender.Factory,
paramsContainer: ParamsContainer,
) : Model(), FeeSelectorModelCallback {
) : Model(), FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback {
private val params: SendWithSwapConfirmComponent.Params = paramsContainer.require()
@ -143,6 +146,12 @@ internal class SendWithSwapConfirmModel @Inject constructor(
uiState.value = sendWithSwapUM
}
override fun onFeeReload() {
modelScope.launch {
feeSelectorReloadTrigger.triggerUpdate()
}
}
fun showEditAmount() {
router.push(SendWithSwapRoute.Amount(isEditMode = true))
}

View file

@ -30,9 +30,9 @@ import com.tangem.domain.walletconnect.usecase.method.*
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.feeselector.FeeSelectorReloadData
import com.tangem.features.send.v2.api.feeselector.FeeSelectorReloadTrigger
import com.tangem.features.send.v2.api.params.FeeSelectorParams
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.v2.api.subcomponents.feeSelector.entity.FeeSelectorData
import com.tangem.features.walletconnect.connections.routing.WcInnerRoute
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter
@ -134,7 +134,9 @@ internal class WcSendTransactionModel @Inject constructor(
private fun triggerFeeReload() {
feeReloadState.value = false
modelScope.launch {
feeSelectorReloadTrigger.triggerUpdate(data = FeeSelectorReloadData(removeSuggestedFee = true))
feeSelectorReloadTrigger.triggerUpdate(
feeData = FeeSelectorData(removeSuggestedFee = true),
)
}
}