Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-20 11:56:20 +05:00
parent f5eaca92f6
commit dea7034aad
26 changed files with 349 additions and 716 deletions

View file

@ -1,56 +0,0 @@
package com.tangem.common.ui.alerts
import com.tangem.common.ui.alerts.models.AlertDemoModeUM
import com.tangem.common.ui.alerts.models.AlertTransactionErrorUM
import com.tangem.common.ui.alerts.models.AlertUM
import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.utils.converter.Converter
class TransactionErrorAlertConverter(
private val popBackStack: () -> Unit,
private val onFailedTxEmailClick: (String) -> Unit,
) : Converter<SendTransactionError, AlertUM?> {
override fun convert(value: SendTransactionError): AlertUM? {
return when (value) {
is SendTransactionError.DemoCardError -> AlertDemoModeUM(
onConfirmClick = popBackStack,
)
is SendTransactionError.TangemSdkError -> AlertTransactionErrorUM(
code = value.code.toString(),
cause = null,
causeTextReference = resourceReference(value.messageRes, wrappedList(value.args)),
onConfirmClick = { onFailedTxEmailClick(value.code.toString()) },
)
is SendTransactionError.BlockchainSdkError -> AlertTransactionErrorUM(
code = value.code.toString(),
cause = value.message,
onConfirmClick = { onFailedTxEmailClick("${value.code}: ${value.message.orEmpty()}") },
)
is SendTransactionError.DataError -> AlertTransactionErrorUM(
code = "",
cause = value.message,
onConfirmClick = { onFailedTxEmailClick(value.message.orEmpty()) },
)
is SendTransactionError.NetworkError -> AlertTransactionErrorUM(
code = value.code.orEmpty(),
cause = value.message.orEmpty(),
onConfirmClick = { onFailedTxEmailClick(value.message.orEmpty()) },
)
is SendTransactionError.UnknownError -> AlertTransactionErrorUM(
code = "",
cause = value.ex?.localizedMessage,
onConfirmClick = { onFailedTxEmailClick(value.ex?.localizedMessage.orEmpty()) },
)
is SendTransactionError.CreateAccountUnderfunded -> AlertTransactionErrorUM(
code = "",
cause = null,
causeTextReference = resourceReference(R.string.no_account_polkadot, wrappedList(value.amount)),
onConfirmClick = popBackStack,
)
else -> null
}
}
}

View file

@ -0,0 +1,83 @@
package com.tangem.common.ui.alerts
import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.TextReference
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.domain.transaction.error.SendTransactionError
import javax.inject.Inject
class TransactionErrorDialogFactory @Inject constructor() {
fun create(
error: SendTransactionError,
popBackStack: () -> Unit,
onFailedTxEmailClick: (String) -> Unit,
): DialogMessage? {
return when (error) {
is SendTransactionError.DemoCardError -> demoModeDialog(popBackStack)
is SendTransactionError.TangemSdkError -> transactionErrorDialog(
causeTextReference = resourceReference(error.messageRes, wrappedList(error.args)),
code = error.code.toString(),
onConfirmClick = { onFailedTxEmailClick(error.code.toString()) },
)
is SendTransactionError.BlockchainSdkError -> transactionErrorDialog(
cause = error.message,
code = error.code.toString(),
onConfirmClick = { onFailedTxEmailClick("${error.code}: ${error.message.orEmpty()}") },
)
is SendTransactionError.DataError -> transactionErrorDialog(
cause = error.message,
code = "",
onConfirmClick = { onFailedTxEmailClick(error.message.orEmpty()) },
)
is SendTransactionError.NetworkError -> transactionErrorDialog(
cause = error.message.orEmpty(),
code = error.code.orEmpty(),
onConfirmClick = { onFailedTxEmailClick(error.message.orEmpty()) },
)
is SendTransactionError.UnknownError -> transactionErrorDialog(
cause = error.ex?.localizedMessage,
code = "",
onConfirmClick = { onFailedTxEmailClick(error.ex?.localizedMessage.orEmpty()) },
)
is SendTransactionError.CreateAccountUnderfunded -> transactionErrorDialog(
causeTextReference = resourceReference(
R.string.no_account_polkadot,
wrappedList(error.amount),
),
code = "",
onConfirmClick = popBackStack,
)
else -> null
}
}
private fun demoModeDialog(onConfirmClick: () -> Unit): DialogMessage = DialogMessage(
title = resourceReference(id = R.string.warning_demo_mode_title),
message = resourceReference(id = R.string.warning_demo_mode_message),
firstAction = EventMessageAction(
title = resourceReference(id = R.string.common_ok),
onClick = onConfirmClick,
),
)
private fun transactionErrorDialog(
cause: String? = null,
causeTextReference: TextReference? = null,
code: String,
onConfirmClick: () -> Unit,
): DialogMessage = DialogMessage(
title = resourceReference(id = R.string.send_alert_transaction_failed_title),
message = resourceReference(
id = R.string.send_alert_transaction_failed_text,
formatArgs = wrappedList(causeTextReference ?: cause.orEmpty(), code),
),
firstAction = EventMessageAction(
title = resourceReference(id = R.string.common_support),
onClick = onConfirmClick,
),
)
}

View file

@ -1,13 +0,0 @@
package com.tangem.common.ui.alerts.models
import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
data class AlertDemoModeUM(
override val onConfirmClick: () -> Unit,
) : AlertUM {
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
override val title: TextReference = resourceReference(id = R.string.warning_demo_mode_title)
override val message: TextReference = resourceReference(id = R.string.warning_demo_mode_message)
}

View file

@ -1,21 +0,0 @@
package com.tangem.common.ui.alerts.models
import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
data class AlertTransactionErrorUM(
val code: String,
val cause: String?,
val causeTextReference: TextReference? = null,
override val onConfirmClick: () -> Unit,
) : AlertUM {
override val title: TextReference = resourceReference(id = R.string.send_alert_transaction_failed_title)
override val message: TextReference = resourceReference(
id = R.string.send_alert_transaction_failed_text,
formatArgs = wrappedList(causeTextReference ?: cause.orEmpty(), code),
)
override val confirmButtonText: TextReference =
resourceReference(id = R.string.common_support)
}

View file

@ -1,12 +0,0 @@
package com.tangem.common.ui.alerts.models
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
@Immutable
interface AlertUM {
val title: TextReference?
val message: TextReference
val confirmButtonText: TextReference
val onConfirmClick: (() -> Unit)?
}

View file

@ -1,15 +1,14 @@
package com.tangem.features.onboarding.v2.common.ui package com.tangem.features.onboarding.v2.common.ui
import com.tangem.common.ui.alerts.models.AlertUM
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
internal data class OnboardingDialogUM( internal data class OnboardingDialogUM(
override val title: TextReference, val title: TextReference,
override val message: TextReference, val message: TextReference,
val dismissButtonText: TextReference, val dismissButtonText: TextReference,
override val confirmButtonText: TextReference, val confirmButtonText: TextReference,
val dismissWarningColor: Boolean = false, val dismissWarningColor: Boolean = false,
override val onConfirmClick: () -> Unit, val onConfirmClick: () -> Unit,
val onDismissButtonClick: () -> Unit, val onDismissButtonClick: () -> Unit,
val onDismiss: () -> Unit, val onDismiss: () -> Unit,
) : AlertUM )

View file

@ -3,7 +3,6 @@ package com.tangem.features.onramp.selecttoken.model
import arrow.core.getOrElse import arrow.core.getOrElse
import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.alerts.models.AlertDemoModeUM
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
@ -13,8 +12,8 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.demo.IsDemoCardUseCase
@ -159,18 +158,9 @@ internal class OnrampOperationModel @Inject constructor(
private fun showErrorIfDemoModeOrElse(action: () -> Unit) { private fun showErrorIfDemoModeOrElse(action: () -> Unit) {
if (selectedUserWallet is UserWallet.Cold && isDemoCardUseCase(cardId = selectedUserWallet.cardId)) { if (selectedUserWallet is UserWallet.Cold && isDemoCardUseCase(cardId = selectedUserWallet.cardId)) {
val alertUM = AlertDemoModeUM(onConfirmClick = {})
val message = DialogMessage( val message = DialogMessage(
title = alertUM.title, title = resourceReference(id = R.string.warning_demo_mode_title),
message = alertUM.message, message = resourceReference(id = R.string.warning_demo_mode_message),
firstActionBuilder = {
EventMessageAction(
title = alertUM.confirmButtonText,
onClick = alertUM.onConfirmClick,
)
},
secondActionBuilder = { cancelAction() },
) )
messageSender.send(message) messageSender.send(message)

View file

@ -1,7 +1,6 @@
package com.tangem.features.send.v2.common package com.tangem.features.send.v2.common
import com.tangem.common.ui.alerts.TransactionErrorAlertConverter import com.tangem.common.ui.alerts.TransactionErrorDialogFactory
import com.tangem.common.ui.alerts.models.AlertDemoModeUM
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
@ -14,6 +13,7 @@ import javax.inject.Inject
@ModelScoped @ModelScoped
internal class SendConfirmAlertFactory @Inject constructor( internal class SendConfirmAlertFactory @Inject constructor(
private val messageSender: UiMessageSender, private val messageSender: UiMessageSender,
private val transactionErrorDialogFactory: TransactionErrorDialogFactory,
) { ) {
fun getGenericErrorState(onFailedTxEmailClick: () -> Unit, popBack: () -> Unit = {}) { fun getGenericErrorState(onFailedTxEmailClick: () -> Unit, popBack: () -> Unit = {}) {
@ -31,34 +31,16 @@ internal class SendConfirmAlertFactory @Inject constructor(
} }
fun getSendTransactionErrorState( fun getSendTransactionErrorState(
error: SendTransactionError?, error: SendTransactionError,
popBack: () -> Unit, popBack: () -> Unit,
onFailedTxEmailClick: (String) -> Unit, onFailedTxEmailClick: (String) -> Unit,
) { ) {
val transactionErrorAlertConverter = TransactionErrorAlertConverter( val errorDialog = transactionErrorDialogFactory.create(
error = error,
popBackStack = popBack, popBackStack = popBack,
onFailedTxEmailClick = onFailedTxEmailClick, onFailedTxEmailClick = onFailedTxEmailClick,
) ) ?: return
val errorAlert = error?.let { transactionErrorAlertConverter.convert(error) } ?: return messageSender.send(errorDialog)
val onConfirmClick = errorAlert.onConfirmClick ?: return
messageSender.send(
DialogMessage(
title = errorAlert.title,
message = errorAlert.message,
firstActionBuilder = {
EventMessageAction(
title = errorAlert.confirmButtonText,
onClick = onConfirmClick,
)
},
secondActionBuilder = if (errorAlert !is AlertDemoModeUM) {
{ cancelAction() }
} else {
null
},
),
)
} }
} }

View file

@ -67,7 +67,6 @@ import com.tangem.features.staking.impl.navigation.InnerStakingRouter
import com.tangem.features.staking.impl.presentation.state.* import com.tangem.features.staking.impl.presentation.state.*
import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType
import com.tangem.features.staking.impl.presentation.state.events.StakingAlertUM import com.tangem.features.staking.impl.presentation.state.events.StakingAlertUM
import com.tangem.features.staking.impl.presentation.state.events.StakingEvent
import com.tangem.features.staking.impl.presentation.state.events.StakingEventFactory import com.tangem.features.staking.impl.presentation.state.events.StakingEventFactory
import com.tangem.features.staking.impl.presentation.state.helpers.StakingBalanceUpdater import com.tangem.features.staking.impl.presentation.state.helpers.StakingBalanceUpdater
import com.tangem.features.staking.impl.presentation.state.helpers.StakingFeeLoader import com.tangem.features.staking.impl.presentation.state.helpers.StakingFeeLoader
@ -249,7 +248,7 @@ internal class StakingModel @Inject constructor(
private val stakingEventFactory: StakingEventFactory private val stakingEventFactory: StakingEventFactory
get() = StakingEventFactory( get() = StakingEventFactory(
stateController = stateController, messageSender = messageSender,
popBackStack = ::onBackClick, popBackStack = ::onBackClick,
onFailedTxEmailClick = ::onFailedTxEmailClick, onFailedTxEmailClick = ::onFailedTxEmailClick,
) )
@ -503,11 +502,7 @@ internal class StakingModel @Inject constructor(
cryptoCurrencyStatus = cryptoCurrencyStatus, cryptoCurrencyStatus = cryptoCurrencyStatus,
), ),
) )
stateController.updateEvent( messageSender.send(StakingAlertUM.feeIncreased {})
StakingEvent.ShowAlert(
StakingAlertUM.FeeIncreased(stateController::dismissAlert),
),
)
updateNotifications() updateNotifications()
}, },
onTransactionExpired = { onTransactionExpired = {
@ -571,9 +566,7 @@ internal class StakingModel @Inject constructor(
override fun onAmountEnterClick() { override fun onAmountEnterClick() {
if (integration.preferredTargets.isEmpty()) { if (integration.preferredTargets.isEmpty()) {
stateController.updateEvent( messageSender.send(StakingAlertUM.noAvailableValidators())
StakingEvent.ShowAlert(StakingAlertUM.NoAvailableValidators),
)
} else { } else {
if (uiState.value.actionType is StakingActionCommonType.Enter) { if (uiState.value.actionType is StakingActionCommonType.Enter) {
stateController.updateAll( stateController.updateAll(
@ -1047,11 +1040,7 @@ internal class StakingModel @Inject constructor(
} }
override fun showPrimaryClickAlert() { override fun showPrimaryClickAlert() {
stateController.updateEvent( messageSender.send(StakingAlertUM.stakeMoreClickUnavailable(cryptoCurrencyStatus.currency))
StakingEvent.ShowAlert(
StakingAlertUM.StakeMoreClickUnavailable(cryptoCurrencyStatus.currency),
),
)
} }
override fun onOpenLearnMoreAboutApproveClick() { override fun onOpenLearnMoreAboutApproveClick() {

View file

@ -3,12 +3,9 @@ package com.tangem.features.staking.impl.presentation.state
import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.navigationButtons.NavigationButtonsState import com.tangem.common.ui.navigationButtons.NavigationButtonsState
import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.event.triggeredEvent
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.features.staking.impl.presentation.state.events.StakingEvent
import com.tangem.features.staking.impl.presentation.state.stub.StakingClickIntentsStub import com.tangem.features.staking.impl.presentation.state.stub.StakingClickIntentsStub
import com.tangem.features.staking.impl.presentation.state.transformers.SetButtonsStateTransformer import com.tangem.features.staking.impl.presentation.state.transformers.SetButtonsStateTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.SetTitleTransformer import com.tangem.features.staking.impl.presentation.state.transformers.SetTitleTransformer
@ -72,16 +69,6 @@ internal class StakingStateController @Inject constructor(
mutableUiState.update(function = titleTransformer::transform) mutableUiState.update(function = titleTransformer::transform)
} }
fun updateEvent(event: StakingEvent?) {
mutableUiState.update {
it.copy(event = event?.let { triggeredEvent(event, ::dismissAlert) } ?: consumedEvent())
}
}
fun dismissAlert() {
mutableUiState.update { it.copy(event = consumedEvent()) }
}
private fun getInitialState(): StakingUiState { private fun getInitialState(): StakingUiState {
return StakingUiState( return StakingUiState(
title = TextReference.EMPTY, title = TextReference.EMPTY,
@ -98,7 +85,6 @@ internal class StakingStateController @Inject constructor(
rewardsValidatorsState = StakingStates.RewardsValidatorsState.Empty(), rewardsValidatorsState = StakingStates.RewardsValidatorsState.Empty(),
confirmationState = StakingStates.ConfirmationState.Empty(), confirmationState = StakingStates.ConfirmationState.Empty(),
isBalanceHidden = false, isBalanceHidden = false,
event = consumedEvent(),
bottomSheetConfig = null, bottomSheetConfig = null,
actionType = StakingActionCommonType.Enter(skipEnterAmount = false), actionType = StakingActionCommonType.Enter(skipEnterAmount = false),
buttonsState = NavigationButtonsState.Empty, buttonsState = NavigationButtonsState.Empty,

View file

@ -6,14 +6,12 @@ import com.tangem.common.ui.navigationButtons.NavigationButtonsState
import com.tangem.common.ui.notifications.NotificationUM import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.list.RoundedListWithDividersItemData import com.tangem.core.ui.components.list.RoundedListWithDividersItemData
import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.domain.models.staking.PendingAction import com.tangem.domain.models.staking.PendingAction
import com.tangem.domain.staking.model.StakingTarget import com.tangem.domain.staking.model.StakingTarget
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType
import com.tangem.features.staking.impl.presentation.state.events.StakingEvent
import com.tangem.features.staking.impl.presentation.model.StakingClickIntents import com.tangem.features.staking.impl.presentation.model.StakingClickIntents
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import java.math.BigDecimal import java.math.BigDecimal
@ -40,7 +38,6 @@ internal data class StakingUiState(
val bottomSheetConfig: TangemBottomSheetConfig?, val bottomSheetConfig: TangemBottomSheetConfig?,
val actionType: StakingActionCommonType, val actionType: StakingActionCommonType,
val buttonsState: NavigationButtonsState, val buttonsState: NavigationButtonsState,
val event: StateEvent<StakingEvent>,
val balanceState: BalanceState?, val balanceState: BalanceState?,
val showColdWalletInteractionIcon: Boolean, val showColdWalletInteractionIcon: Boolean,
val shouldShowHoldToConfirmButton: Boolean, val shouldShowHoldToConfirmButton: Boolean,

View file

@ -1,85 +1,74 @@
package com.tangem.features.staking.impl.presentation.state.events package com.tangem.features.staking.impl.presentation.state.events
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.alerts.models.AlertUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.R
@Immutable internal object StakingAlertUM {
internal sealed class StakingAlertUM : AlertUM {
data class GenericError( fun genericError(onConfirmClick: () -> Unit): DialogMessage = DialogMessage(
override val onConfirmClick: () -> Unit, title = resourceReference(R.string.common_error),
) : StakingAlertUM() { message = resourceReference(R.string.common_unknown_error),
override val title: TextReference = resourceReference(R.string.common_error) firstAction = EventMessageAction(
override val message: TextReference = resourceReference(R.string.common_unknown_error) title = resourceReference(id = R.string.common_support),
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_support) onClick = onConfirmClick,
} ),
)
data class StakingError( fun stakingError(code: String, onConfirmClick: () -> Unit): DialogMessage = DialogMessage(
val code: String, title = resourceReference(R.string.common_error),
override val onConfirmClick: () -> Unit, message = resourceReference(R.string.generic_error_code, wrappedList(code)),
) : StakingAlertUM() { firstAction = EventMessageAction(
override val title: TextReference = resourceReference(R.string.common_error) title = resourceReference(id = R.string.common_support),
override val message: TextReference = resourceReference(R.string.generic_error_code, wrappedList(code)) onClick = onConfirmClick,
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_support) ),
} )
data object NoAvailableValidators : StakingAlertUM() { fun noAvailableValidators(): DialogMessage = DialogMessage(
override val title = resourceReference(R.string.common_error) title = resourceReference(R.string.common_error),
override val message = resourceReference(R.string.staking_no_validators_error_message) message = resourceReference(R.string.staking_no_validators_error_message),
override val confirmButtonText = resourceReference(R.string.common_ok) )
override val onConfirmClick = null
}
data class FeeIncreased( fun feeIncreased(onConfirmClick: () -> Unit): DialogMessage = DialogMessage(
override val onConfirmClick: () -> Unit, title = null,
) : StakingAlertUM() { message = resourceReference(id = R.string.send_notification_high_fee_title),
override val title: TextReference? = null firstAction = EventMessageAction(
override val message: TextReference = resourceReference(id = R.string.send_notification_high_fee_title) title = resourceReference(id = R.string.common_ok),
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok) onClick = onConfirmClick,
} ),
)
data object ValidatorsUnavailable : StakingAlertUM() { fun validatorsUnavailable(): DialogMessage = DialogMessage(
override val onConfirmClick: (() -> Unit)? = null title = resourceReference(id = R.string.staking_error_no_validators_title),
override val title: TextReference = resourceReference(id = R.string.staking_error_no_validators_title) message = resourceReference(id = R.string.staking_error_no_validators_message),
override val message: TextReference = resourceReference(id = R.string.staking_error_no_validators_message) )
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
}
data class StakeMoreClickUnavailable( fun stakeMoreClickUnavailable(cryptoCurrency: CryptoCurrency): DialogMessage = DialogMessage(
val cryptoCurrency: CryptoCurrency, title = null,
) : StakingAlertUM() { message = resourceReference(
override val onConfirmClick: (() -> Unit)? = null
override val title: TextReference? = null
override val message: TextReference = resourceReference(
id = R.string.staking_stake_more_button_unavailability_reason, id = R.string.staking_stake_more_button_unavailability_reason,
wrappedList(cryptoCurrency.name, cryptoCurrency.symbol), wrappedList(cryptoCurrency.name, cryptoCurrency.symbol),
) ),
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok) )
}
data class RewardsMinimumRequirementsError( fun rewardsMinimumRequirementsError(cryptoCurrencyName: String, cryptoAmountValue: String): DialogMessage =
val cryptoCurrencyName: String, DialogMessage(
val cryptoAmountValue: String, title = null,
) : StakingAlertUM() { message = resourceReference(
override val onConfirmClick: (() -> Unit)? = null id = R.string.staking_details_min_rewards_notification,
override val title: TextReference? = null formatArgs = wrappedList(cryptoCurrencyName, cryptoAmountValue),
override val message: TextReference = resourceReference( ),
id = R.string.staking_details_min_rewards_notification,
formatArgs = wrappedList(cryptoCurrencyName, cryptoAmountValue),
) )
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
}
data class NetworkFeeUpdated( fun networkFeeUpdated(onConfirmClick: () -> Unit): DialogMessage = DialogMessage(
override val onConfirmClick: () -> Unit, title = resourceReference(R.string.staking_alert_network_fee_updated_title),
) : StakingAlertUM() { message = resourceReference(R.string.staking_alert_network_fee_updated_message),
override val title: TextReference = resourceReference(R.string.staking_alert_network_fee_updated_title) firstAction = EventMessageAction(
override val message: TextReference = resourceReference(R.string.staking_alert_network_fee_updated_message) title = resourceReference(id = R.string.common_ok),
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok) onClick = onConfirmClick,
} ),
)
} }

View file

@ -1,13 +0,0 @@
package com.tangem.features.staking.impl.presentation.state.events
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.alerts.models.AlertUM
import com.tangem.core.ui.extensions.TextReference
@Immutable
internal sealed class StakingEvent {
data class ShowSnackBar(val text: TextReference) : StakingEvent()
data class ShowAlert(val alert: AlertUM) : StakingEvent()
}

View file

@ -1,69 +1,63 @@
package com.tangem.features.staking.impl.presentation.state.events package com.tangem.features.staking.impl.presentation.state.events
import com.tangem.common.ui.alerts.TransactionErrorAlertConverter import com.tangem.common.ui.alerts.TransactionErrorDialogFactory
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.StakingError
import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.features.staking.impl.presentation.state.StakingStateController
internal class StakingEventFactory( internal class StakingEventFactory(
private val stateController: StakingStateController, private val messageSender: UiMessageSender,
private val popBackStack: () -> Unit, private val popBackStack: () -> Unit,
private val onFailedTxEmailClick: (String) -> Unit, private val onFailedTxEmailClick: (String) -> Unit,
private val transactionErrorDialogFactory: TransactionErrorDialogFactory = TransactionErrorDialogFactory(),
) { ) {
fun createGenericErrorAlert(error: String) { fun createGenericErrorAlert(error: String) {
val alert = StakingEvent.ShowAlert( messageSender.send(
StakingAlertUM.GenericError( StakingAlertUM.genericError(
onConfirmClick = { onFailedTxEmailClick(error) }, onConfirmClick = { onFailedTxEmailClick(error) },
), ),
) )
stateController.updateEvent(alert)
} }
fun createSendTransactionErrorAlert(error: SendTransactionError?) { fun createSendTransactionErrorAlert(error: SendTransactionError?) {
val alert = error?.let { val alert = error?.let {
TransactionErrorAlertConverter( transactionErrorDialogFactory.create(
error = error,
popBackStack = popBackStack, popBackStack = popBackStack,
onFailedTxEmailClick = onFailedTxEmailClick, onFailedTxEmailClick = onFailedTxEmailClick,
).convert(error) )
}?.let {
StakingEvent.ShowAlert(it)
} }
stateController.updateEvent(alert) alert?.let { messageSender.send(it) }
} }
fun createStakingErrorAlert(error: StakingError) { fun createStakingErrorAlert(error: StakingError) {
val alert = StakingEvent.ShowAlert( messageSender.send(
StakingAlertUM.StakingError( StakingAlertUM.stakingError(
code = error.toString(), code = error.toString(),
onConfirmClick = { onFailedTxEmailClick(error.toString()) }, onConfirmClick = { onFailedTxEmailClick(error.toString()) },
), ),
) )
stateController.updateEvent(alert)
} }
fun createStakingValidatorsUnavailableAlert() { fun createStakingValidatorsUnavailableAlert() {
val alert = StakingEvent.ShowAlert(alert = StakingAlertUM.ValidatorsUnavailable) messageSender.send(StakingAlertUM.validatorsUnavailable())
stateController.updateEvent(alert)
} }
fun createStakingRewardsMinimumRequirementsErrorAlert(cryptoCurrencyName: String, cryptoAmountValue: String) { fun createStakingRewardsMinimumRequirementsErrorAlert(cryptoCurrencyName: String, cryptoAmountValue: String) {
stateController.updateEvent( messageSender.send(
StakingEvent.ShowAlert( StakingAlertUM.rewardsMinimumRequirementsError(
alert = StakingAlertUM.RewardsMinimumRequirementsError( cryptoCurrencyName = cryptoCurrencyName,
cryptoCurrencyName = cryptoCurrencyName, cryptoAmountValue = cryptoAmountValue,
cryptoAmountValue = cryptoAmountValue,
),
), ),
) )
} }
fun createNetworkFeeUpdatedAlert(onConfirm: () -> Unit) { fun createNetworkFeeUpdatedAlert(onConfirm: () -> Unit) {
val alert = StakingEvent.ShowAlert( messageSender.send(
alert = StakingAlertUM.NetworkFeeUpdated( StakingAlertUM.networkFeeUpdated(
onConfirmClick = onConfirm, onConfirmClick = onConfirm,
), ),
) )
stateController.updateEvent(alert)
} }
} }

View file

@ -1,80 +0,0 @@
package com.tangem.features.staking.impl.presentation.ui
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import com.tangem.common.ui.alerts.models.AlertUM
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.event.EventEffect
import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.state.events.StakingEvent
@Composable
internal fun StakingEventEffect(event: StateEvent<StakingEvent>, snackbarHostState: SnackbarHostState) {
val resources = LocalContext.current.resources
var alertConfig by remember { mutableStateOf<AlertUM?>(value = null) }
val keyboardController = LocalSoftwareKeyboardController.current
LaunchedEffect(key1 = alertConfig) {
keyboardController?.hide()
}
alertConfig?.let {
StakingAlert(state = it, onDismiss = { alertConfig = null })
}
EventEffect(
event = event,
onTrigger = { value ->
when (value) {
is StakingEvent.ShowSnackBar -> {
snackbarHostState.showSnackbar(message = value.text.resolveReference(resources))
}
is StakingEvent.ShowAlert -> {
alertConfig = value.alert
}
}
},
)
}
@Composable
internal fun StakingAlert(state: AlertUM, onDismiss: () -> Unit) {
val confirmButton: DialogButtonUM
val dismissButton: DialogButtonUM?
val onActionClick = state.onConfirmClick
if (onActionClick != null) {
confirmButton = DialogButtonUM(
title = state.confirmButtonText.resolveReference(),
onClick = {
onActionClick()
onDismiss()
},
)
dismissButton = DialogButtonUM(
title = stringResourceSafe(id = R.string.common_cancel),
onClick = onDismiss,
)
} else {
confirmButton = DialogButtonUM(
title = state.confirmButtonText.resolveReference(),
onClick = onDismiss,
)
dismissButton = null
}
BasicDialog(
message = state.message.resolveReference(),
confirmButton = confirmButton,
onDismissDialog = onDismiss,
title = state.title?.resolveReference(),
dismissButton = dismissButton,
)
}

View file

@ -7,7 +7,6 @@ import androidx.compose.animation.core.tween
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -39,7 +38,6 @@ import kotlinx.coroutines.flow.withIndex
@Composable @Composable
internal fun StakingScreen(uiState: StakingUiState) { internal fun StakingScreen(uiState: StakingUiState) {
val snackbarHostState = remember { SnackbarHostState() }
val confirmationState = uiState.confirmationState as? StakingStates.ConfirmationState.Data val confirmationState = uiState.confirmationState as? StakingStates.ConfirmationState.Data
BackHandler(onBack = uiState.clickIntents::onPrevClick) BackHandler(onBack = uiState.clickIntents::onPrevClick)
@ -71,11 +69,6 @@ internal fun StakingScreen(uiState: StakingUiState) {
) )
StakingBottomSheet(bottomSheetConfig = uiState.bottomSheetConfig) StakingBottomSheet(bottomSheetConfig = uiState.bottomSheetConfig)
} }
StakingEventEffect(
event = uiState.event,
snackbarHostState = snackbarHostState,
)
} }
@Composable @Composable

View file

@ -1,7 +1,6 @@
package com.tangem.features.swap.v2.impl.common package com.tangem.features.swap.v2.impl.common
import com.tangem.common.ui.alerts.TransactionErrorAlertConverter import com.tangem.common.ui.alerts.TransactionErrorDialogFactory
import com.tangem.common.ui.alerts.models.AlertDemoModeUM
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
@ -25,6 +24,7 @@ internal class SwapAlertFactory @Inject constructor(
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase, private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase, private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val transactionErrorDialogFactory: TransactionErrorDialogFactory,
) { ) {
fun getGenericErrorState(expressError: ExpressError, onFailedTxEmailClick: () -> Unit, popBack: () -> Unit = {}) { fun getGenericErrorState(expressError: ExpressError, onFailedTxEmailClick: () -> Unit, popBack: () -> Unit = {}) {
uiMessageSender.send( uiMessageSender.send(
@ -44,36 +44,21 @@ internal class SwapAlertFactory @Inject constructor(
) )
} }
@Suppress("CanBeNonNullable")
fun getSendTransactionErrorState( fun getSendTransactionErrorState(
error: SendTransactionError?, error: SendTransactionError?,
popBack: () -> Unit, popBack: () -> Unit,
onFailedTxEmailClick: (String) -> Unit, onFailedTxEmailClick: (String) -> Unit,
) { ) {
val transactionErrorAlertConverter = TransactionErrorAlertConverter( if (error == null) return
val errorDialog = transactionErrorDialogFactory.create(
error = error,
popBackStack = popBack, popBackStack = popBack,
onFailedTxEmailClick = onFailedTxEmailClick, onFailedTxEmailClick = onFailedTxEmailClick,
) ) ?: return
val errorAlert = error?.let { transactionErrorAlertConverter.convert(error) } ?: return uiMessageSender.send(errorDialog)
val onConfirmClick = errorAlert.onConfirmClick ?: return
uiMessageSender.send(
DialogMessage.Companion(
title = errorAlert.title,
message = errorAlert.message,
firstActionBuilder = {
EventMessageAction(
title = errorAlert.confirmButtonText,
onClick = onConfirmClick,
)
},
secondActionBuilder = if (errorAlert !is AlertDemoModeUM) {
{ cancelAction() }
} else {
null
},
),
)
} }
suspend fun onFailedTxEmailClick( suspend fun onFailedTxEmailClick(

View file

@ -1,7 +1,7 @@
package com.tangem.feature.swap.converters package com.tangem.feature.swap.converters
import com.tangem.common.ui.alerts.TransactionErrorAlertConverter import com.tangem.common.ui.alerts.TransactionErrorDialogFactory
import com.tangem.common.ui.alerts.models.AlertUM import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.feature.swap.domain.models.ui.SwapTransactionState import com.tangem.feature.swap.domain.models.ui.SwapTransactionState
import com.tangem.feature.swap.models.SwapAlertUM import com.tangem.feature.swap.models.SwapAlertUM
@ -11,24 +11,25 @@ import com.tangem.utils.converter.Converter
internal class SwapTransactionErrorStateConverter( internal class SwapTransactionErrorStateConverter(
private val onDismiss: () -> Unit, private val onDismiss: () -> Unit,
private val onSupportClick: (String) -> Unit, private val onSupportClick: (String) -> Unit,
) : Converter<SwapTransactionState.Error, AlertUM?> { private val transactionErrorDialogFactory: TransactionErrorDialogFactory = TransactionErrorDialogFactory(),
override fun convert(value: SwapTransactionState.Error): AlertUM? { ) : Converter<SwapTransactionState.Error, DialogMessage?> {
override fun convert(value: SwapTransactionState.Error): DialogMessage? {
return when (value) { return when (value) {
is SwapTransactionState.Error.TransactionError -> { is SwapTransactionState.Error.TransactionError -> {
when (val error = value.error) { when (val error = value.error) {
is SendTransactionError.UserCancelledError -> return null is SendTransactionError.UserCancelledError -> return null
null -> SwapAlertUM.GenericError(onDismiss) null -> SwapAlertUM.genericError(onDismiss)
else -> TransactionErrorAlertConverter(onDismiss, onSupportClick).convert(error) else -> transactionErrorDialogFactory.create(error, onDismiss, onSupportClick)
} }
} }
is SwapTransactionState.Error.ExpressError -> { is SwapTransactionState.Error.ExpressError -> {
SwapAlertUM.ExpressErrorAlert( SwapAlertUM.expressErrorAlert(
message = getExpressErrorMessage(value.error), message = getExpressErrorMessage(value.error),
onConfirmClick = { onSupportClick(value.error.code.toString()) }, onConfirmClick = { onSupportClick(value.error.code.toString()) },
) )
} }
SwapTransactionState.Error.UnknownError -> SwapAlertUM.GenericError(onDismiss) SwapTransactionState.Error.UnknownError -> SwapAlertUM.genericError(onDismiss)
is SwapTransactionState.Error.TangemPayWithdrawalError -> SwapAlertUM.GenericError( is SwapTransactionState.Error.TangemPayWithdrawalError -> SwapAlertUM.genericError(
onConfirmClick = { onSupportClick(value.txId) }, onConfirmClick = { onSupportClick(value.txId) },
) )
} }

View file

@ -23,11 +23,21 @@ import com.tangem.core.analytics.models.Basic
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.core.ui.extensions.TextReference 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.resourceReference
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 import com.tangem.core.ui.utils.InputNumberFormatter
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.feature.swap.converters.SwapTransactionErrorStateConverter
import com.tangem.feature.swap.models.SwapAlertUM
import com.tangem.datasource.local.appsflyer.AppsFlyerStore import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
@ -165,6 +175,7 @@ internal class SwapModel @Inject constructor(
private val getTangemPayCustomerIdUseCase: GetTangemPayCustomerIdUseCase, private val getTangemPayCustomerIdUseCase: GetTangemPayCustomerIdUseCase,
private val appsFlyerStore: AppsFlyerStore, private val appsFlyerStore: AppsFlyerStore,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles, private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
private val messageSender: UiMessageSender,
) : Model() { ) : Model() {
private val params = paramsContainer.require<SwapComponent.Params>() private val params = paramsContainer.require<SwapComponent.Params>()
@ -349,7 +360,8 @@ internal class SwapModel @Inject constructor(
} }
if (fromAccountStatus == null) { if (fromAccountStatus == null) {
uiState = stateBuilder.addAlert(uiState = uiState, onDismiss = swapRouter::back) showAlert()
swapRouter.back()
} else { } else {
fromAccountCurrencyStatus = fromAccountStatus fromAccountCurrencyStatus = fromAccountStatus
toAccountCurrencyStatus = toAccountStatus toAccountCurrencyStatus = toAccountStatus
@ -367,7 +379,8 @@ internal class SwapModel @Inject constructor(
} }
if (fromStatus == null) { if (fromStatus == null) {
uiState = stateBuilder.addAlert(uiState = uiState, onDismiss = swapRouter::back) showAlert()
swapRouter.back()
} else { } else {
initialFromStatus = fromStatus initialFromStatus = fromStatus
initialToStatus = toStatus initialToStatus = toStatus
@ -1032,7 +1045,7 @@ internal class SwapModel @Inject constructor(
val fee = getSelectedFee() val fee = getSelectedFee()
if (fee == null && tangemPayInput?.isWithdrawal != true) { if (fee == null && tangemPayInput?.isWithdrawal != true) {
makeDefaultAlert(resourceReference(R.string.swapping_fee_estimation_error_text)) showAlert(resourceReference(R.string.swapping_fee_estimation_error_text))
modelScope.launch { modelScope.launch {
delay(SWAP_IN_PROGRESS_DELAY) delay(SWAP_IN_PROGRESS_DELAY)
startLoadingQuotesFromLastState() startLoadingQuotesFromLastState()
@ -1058,7 +1071,7 @@ internal class SwapModel @Inject constructor(
when (swapTransactionState) { when (swapTransactionState) {
is SwapTransactionState.TxSent -> { is SwapTransactionState.TxSent -> {
if (fee == null) { if (fee == null) {
makeDefaultAlert(resourceReference(R.string.swapping_fee_estimation_error_text)) showAlert(resourceReference(R.string.swapping_fee_estimation_error_text))
return@onSuccess return@onSuccess
} }
sendSuccessSwapEvent( sendSuccessSwapEvent(
@ -1102,21 +1115,11 @@ internal class SwapModel @Inject constructor(
swapRouter.openScreen(SwapNavScreen.Success) swapRouter.openScreen(SwapNavScreen.Success)
} }
SwapTransactionState.DemoMode -> { SwapTransactionState.DemoMode -> {
uiState = stateBuilder.createDemoModeAlert( showDemoModeAlert()
uiState = uiState,
onDismiss = { uiState = stateBuilder.clearAlert(uiState) },
isReverseSwapPossible = isReverseSwapPossible(),
)
} }
is SwapTransactionState.Error -> { is SwapTransactionState.Error -> {
startLoadingQuotesFromLastState() startLoadingQuotesFromLastState()
uiState = stateBuilder.createErrorTransactionAlert( showTransactionErrorAlert(swapTransactionState)
uiState = uiState,
error = swapTransactionState,
onDismiss = { uiState = stateBuilder.clearAlert(uiState) },
onSupportClick = ::onFailedTxEmailClick,
isReverseSwapPossible = isReverseSwapPossible(),
)
} }
is SwapTransactionState.TangemPayWithdrawalData -> { is SwapTransactionState.TangemPayWithdrawalData -> {
processTangemPayWithdrawal(swapTransactionState = swapTransactionState) processTangemPayWithdrawal(swapTransactionState = swapTransactionState)
@ -1125,7 +1128,7 @@ internal class SwapModel @Inject constructor(
}.onFailure { error -> }.onFailure { error ->
Timber.e(error) Timber.e(error)
startLoadingQuotesFromLastState() startLoadingQuotesFromLastState()
makeDefaultAlert() showAlert()
} }
} }
} }
@ -1217,7 +1220,7 @@ internal class SwapModel @Inject constructor(
} }
val feeForPermission = when (val fee = approveDataModel.fee) { val feeForPermission = when (val fee = approveDataModel.fee) {
TxFeeState.Empty -> { TxFeeState.Empty -> {
makeDefaultAlert(resourceReference(R.string.swapping_fee_estimation_error_text)) showAlert(resourceReference(R.string.swapping_fee_estimation_error_text))
Timber.e("Fee should not be Empty") Timber.e("Fee should not be Empty")
return@launch return@launch
} }
@ -1247,29 +1250,19 @@ internal class SwapModel @Inject constructor(
startLoadingQuotesFromLastState(isSilent = true) startLoadingQuotesFromLastState(isSilent = true)
} }
is SwapTransactionState.Error -> { is SwapTransactionState.Error -> {
uiState = stateBuilder.createErrorTransactionAlert( showTransactionErrorAlert(swapTransactionState)
uiState = uiState,
error = swapTransactionState,
onDismiss = { uiState = stateBuilder.clearAlert(uiState) },
onSupportClick = ::onFailedTxEmailClick,
isReverseSwapPossible = isReverseSwapPossible(),
)
} }
SwapTransactionState.DemoMode -> { SwapTransactionState.DemoMode -> {
uiState = stateBuilder.createDemoModeAlert( showDemoModeAlert()
uiState = uiState,
onDismiss = { uiState = stateBuilder.clearAlert(uiState) },
isReverseSwapPossible = isReverseSwapPossible(),
)
} }
is SwapTransactionState.TangemPayWithdrawalData -> { is SwapTransactionState.TangemPayWithdrawalData -> {
processTangemPayWithdrawal(swapTransactionState = swapTransactionState) processTangemPayWithdrawal(swapTransactionState = swapTransactionState)
} }
} }
}.onFailure { makeDefaultAlert() } }.onFailure { showAlert() }
}.onFailure { error -> }.onFailure { error ->
Timber.e(error.message.orEmpty()) Timber.e(error.message.orEmpty())
makeDefaultAlert() showAlert()
} }
} }
} }
@ -1652,12 +1645,94 @@ internal class SwapModel @Inject constructor(
return inputNumberFormatter.getValidatedNumberWithFixedDecimals(amount, maxDecimals) return inputNumberFormatter.getValidatedNumberWithFixedDecimals(amount, maxDecimals)
} }
private fun makeDefaultAlert() { private fun showAlert(message: TextReference = resourceReference(R.string.common_unknown_error)) {
uiState = stateBuilder.addAlert(uiState) messageSender.send(SwapAlertUM.genericError(onConfirmClick = { }, message = message))
} }
private fun makeDefaultAlert(message: TextReference) { private fun showDemoModeAlert() {
uiState = stateBuilder.addAlert(uiState, message) messageSender.send(
DialogMessage(
title = resourceReference(id = R.string.warning_demo_mode_title),
message = resourceReference(id = R.string.warning_demo_mode_message),
firstAction = EventMessageAction(
title = resourceReference(id = R.string.common_ok),
onClick = {},
),
),
)
}
private fun showTransactionErrorAlert(
error: SwapTransactionState.Error,
onSupportClick: (String) -> Unit = ::onFailedTxEmailClick,
) {
val errorAlert = SwapTransactionErrorStateConverter(
onDismiss = {},
onSupportClick = onSupportClick,
).convert(error)
errorAlert?.let { messageSender.send(it) }
}
private fun onTangemPaySupportClick(txId: String) {
modelScope.launch {
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
val customerId = getTangemPayCustomerIdUseCase(userWallet.walletId).getOrNull().orEmpty()
val email = FeedbackEmailType.Visa.Withdrawal(
walletMetaInfo = metaInfo,
customerId = customerId,
providerName = dataState.selectedProvider?.name.orEmpty(),
txId = txId,
)
sendFeedbackEmailUseCase(email)
}
}
private fun showSwapInfoAlert(isPriceImpact: Boolean, token: String, provider: SwapProvider) {
messageSender.send(
SwapAlertUM.informationAlert(
message = buildSwapInfoMessage(isPriceImpact, token, provider),
onConfirmClick = {},
),
)
}
private fun buildSwapInfoMessage(isPriceImpact: Boolean, token: String, provider: SwapProvider): TextReference {
val slippage = provider.slippage?.let { "${it.parseBigDecimal(1)}%" }
val messages = buildList {
when (provider.type) {
ExchangeProviderType.CEX -> {
if (slippage != null) {
add(
resourceReference(
id = R.string.swapping_alert_cex_description_with_slippage,
formatArgs = wrappedList(token, slippage),
),
)
} else {
add(resourceReference(R.string.swapping_alert_cex_description, wrappedList(token)))
}
}
ExchangeProviderType.DEX,
ExchangeProviderType.DEX_BRIDGE,
-> {
if (isPriceImpact) {
add(resourceReference(R.string.swapping_high_price_impact_description))
add(stringReference("\n\n"))
}
if (slippage != null) {
add(
resourceReference(
id = R.string.swapping_alert_dex_description_with_slippage,
formatArgs = wrappedList(slippage),
),
)
} else {
add(resourceReference(R.string.swapping_alert_dex_description, wrappedList(token)))
}
}
}
}
return combinedReference(messages.toWrappedList())
} }
@Suppress("LongMethod", "CyclomaticComplexMethod") @Suppress("LongMethod", "CyclomaticComplexMethod")
@ -1777,14 +1852,7 @@ internal class SwapModel @Inject constructor(
val selectedProvider = dataState.selectedProvider ?: return@UiActions val selectedProvider = dataState.selectedProvider ?: return@UiActions
val currencySymbol = dataState.toCryptoCurrency?.currency?.symbol ?: return@UiActions val currencySymbol = dataState.toCryptoCurrency?.currency?.symbol ?: return@UiActions
val isPriceImpact = uiState.priceImpact is PriceImpact.Value val isPriceImpact = uiState.priceImpact is PriceImpact.Value
uiState = stateBuilder.createAlert( showSwapInfoAlert(isPriceImpact, currencySymbol, selectedProvider)
uiState = uiState,
isPriceImpact = isPriceImpact,
token = currencySymbol,
provider = selectedProvider,
isReverseSwapPossible = isReverseSwapPossible(),
onDismiss = { uiState = stateBuilder.clearAlert(uiState) },
)
}, },
onLinkClick = urlOpener::openUrl, onLinkClick = urlOpener::openUrl,
onSelectTokenClick = { onSelectTokenClick = {
@ -2090,31 +2158,12 @@ internal class SwapModel @Inject constructor(
} }
private fun onTangemPayWithdrawalError(txId: String?) { private fun onTangemPayWithdrawalError(txId: String?) {
uiState = stateBuilder.createErrorTransactionAlert( showTransactionErrorAlert(
uiState = uiState,
error = SwapTransactionState.Error.TangemPayWithdrawalError(txId.orEmpty()), error = SwapTransactionState.Error.TangemPayWithdrawalError(txId.orEmpty()),
onDismiss = { uiState = stateBuilder.clearAlert(uiState) }, onSupportClick = ::onTangemPaySupportClick,
onSupportClick = {
val customerId = getTangemPayCustomerIdUseCase(userWallet.walletId).getOrNull() ?: "Unknown"
onTangemPaySupportClick(customerId = customerId, txId = txId)
},
isReverseSwapPossible = isReverseSwapPossible(),
) )
} }
private fun onTangemPaySupportClick(customerId: String, txId: String?) {
modelScope.launch {
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
val email = FeedbackEmailType.Visa.Withdrawal(
walletMetaInfo = metaInfo,
customerId = customerId,
providerName = dataState.selectedProvider?.name.orEmpty(),
txId = txId.orEmpty(),
)
sendFeedbackEmailUseCase(email)
}
}
private fun onFailedTxEmailClick(errorMessage: String) { private fun onFailedTxEmailClick(errorMessage: String) {
modelScope.launch { modelScope.launch {
val transaction = dataState.swapDataModel?.transaction val transaction = dataState.swapDataModel?.transaction

View file

@ -1,38 +1,43 @@
package com.tangem.feature.swap.models package com.tangem.feature.swap.models
import com.tangem.common.ui.alerts.models.AlertUM
import com.tangem.core.ui.R import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
sealed class SwapAlertUM : AlertUM { internal object SwapAlertUM {
data class GenericError( fun genericError(
override val onConfirmClick: (() -> Unit), onConfirmClick: () -> Unit,
override val message: TextReference = resourceReference(R.string.common_unknown_error), message: TextReference = resourceReference(R.string.common_unknown_error),
) : SwapAlertUM() { ): DialogMessage = DialogMessage(
override val title: TextReference? = null title = null,
override val confirmButtonText: TextReference = message = message,
resourceReference(id = R.string.common_support) firstAction = EventMessageAction(
} title = resourceReference(id = R.string.common_support),
onClick = onConfirmClick,
),
)
data class ExpressErrorAlert( fun expressErrorAlert(
override val message: TextReference = resourceReference(R.string.common_unknown_error), message: TextReference = resourceReference(R.string.common_unknown_error),
override val onConfirmClick: (() -> Unit), onConfirmClick: () -> Unit,
) : SwapAlertUM() { ): DialogMessage = DialogMessage(
override val title: TextReference? = null title = null,
override val confirmButtonText: TextReference = message = message,
resourceReference(id = R.string.common_support) firstAction = EventMessageAction(
} title = resourceReference(id = R.string.common_support),
onClick = onConfirmClick,
),
)
data class InformationAlert( fun informationAlert(message: TextReference, onConfirmClick: () -> Unit): DialogMessage = DialogMessage(
override val message: TextReference, title = resourceReference(R.string.swapping_alert_title),
override val onConfirmClick: (() -> Unit), message = message,
) : SwapAlertUM() { firstAction = EventMessageAction(
override val title: TextReference = resourceReference( title = resourceReference(id = R.string.common_ok),
R.string.swapping_alert_title, onClick = onConfirmClick,
) ),
override val confirmButtonText: TextReference = )
resourceReference(id = R.string.common_ok)
}
} }

View file

@ -7,14 +7,11 @@ import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionState
import com.tangem.common.ui.notifications.NotificationUM import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.common.ui.swapStoriesScreen.SwapStoriesUM import com.tangem.common.ui.swapStoriesScreen.SwapStoriesUM
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.feature.swap.domain.models.ui.PriceImpact import com.tangem.feature.swap.domain.models.ui.PriceImpact
import com.tangem.feature.swap.models.states.FeeItemState import com.tangem.feature.swap.models.states.FeeItemState
import com.tangem.feature.swap.models.states.ProviderState import com.tangem.feature.swap.models.states.ProviderState
import com.tangem.feature.swap.models.states.events.SwapEvent
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -24,7 +21,6 @@ internal data class SwapStateHolder(
val blockchainId: String, // not the same as networkId, its local id in app val blockchainId: String, // not the same as networkId, its local id in app
val notifications: ImmutableList<NotificationUM> = persistentListOf(), val notifications: ImmutableList<NotificationUM> = persistentListOf(),
val isInsufficientFunds: Boolean, val isInsufficientFunds: Boolean,
val event: StateEvent<SwapEvent> = consumedEvent(),
val changeCardsButtonState: ChangeCardsButtonState, val changeCardsButtonState: ChangeCardsButtonState,
val providerState: ProviderState, val providerState: ProviderState,

View file

@ -1,9 +0,0 @@
package com.tangem.feature.swap.models.states.events
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.alerts.models.AlertUM
@Immutable
internal sealed class SwapEvent {
data class ShowAlert(val alert: AlertUM) : SwapEvent()
}

View file

@ -5,21 +5,17 @@ import androidx.compose.ui.text.input.TextFieldValue
import com.tangem.common.ui.account.AccountTitleUM import com.tangem.common.ui.account.AccountTitleUM
import com.tangem.common.ui.account.CryptoPortfolioIconConverter import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM import com.tangem.common.ui.account.toUM
import com.tangem.common.ui.alerts.models.AlertDemoModeUM
import com.tangem.common.ui.bottomsheet.permission.state.* import com.tangem.common.ui.bottomsheet.permission.state.*
import com.tangem.common.ui.notifications.NotificationUM import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.common.ui.swapStoriesScreen.SwapStoriesFactory import com.tangem.common.ui.swapStoriesScreen.SwapStoriesFactory
import com.tangem.common.ui.userwallet.ext.walletInterationIcon import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.event.triggeredEvent
import com.tangem.core.ui.extensions.* import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.anyDecimals import com.tangem.core.ui.format.bigdecimal.anyDecimals
import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
@ -29,7 +25,6 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isHotWallet import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.promo.models.StoryContent import com.tangem.domain.promo.models.StoryContent
import com.tangem.domain.transaction.usecase.gasless.IsGaslessFeeSupportedForNetwork import com.tangem.domain.transaction.usecase.gasless.IsGaslessFeeSupportedForNetwork
import com.tangem.feature.swap.converters.SwapTransactionErrorStateConverter
import com.tangem.feature.swap.converters.TokensDataConverter import com.tangem.feature.swap.converters.TokensDataConverter
import com.tangem.feature.swap.converters.TokensDataConverterV2 import com.tangem.feature.swap.converters.TokensDataConverterV2
import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.ExpressDataError
@ -43,12 +38,10 @@ import com.tangem.feature.swap.model.SwapNotificationsFactory
import com.tangem.feature.swap.model.SwapProcessDataState import com.tangem.feature.swap.model.SwapProcessDataState
import com.tangem.feature.swap.models.* import com.tangem.feature.swap.models.*
import com.tangem.feature.swap.models.states.* import com.tangem.feature.swap.models.states.*
import com.tangem.feature.swap.models.states.events.SwapEvent
import com.tangem.feature.swap.presentation.R import com.tangem.feature.swap.presentation.R
import com.tangem.feature.swap.utils.formatToUIRepresentation import com.tangem.feature.swap.utils.formatToUIRepresentation
import com.tangem.utils.Provider import com.tangem.utils.Provider
import com.tangem.utils.StringsSigns.DASH_SIGN import com.tangem.utils.StringsSigns.DASH_SIGN
import com.tangem.utils.StringsSigns.PERCENT
import com.tangem.utils.StringsSigns.TILDE_SIGN import com.tangem.utils.StringsSigns.TILDE_SIGN
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -972,117 +965,6 @@ internal class StateBuilder(
) )
} }
fun createErrorTransactionAlert(
uiState: SwapStateHolder,
error: SwapTransactionState.Error,
onDismiss: () -> Unit,
onSupportClick: (String) -> Unit,
isReverseSwapPossible: Boolean,
): SwapStateHolder {
val errorAlert = SwapTransactionErrorStateConverter(
onSupportClick = onSupportClick,
onDismiss = onDismiss,
).convert(error)
return uiState.copy(
event = errorAlert?.let {
triggeredEvent(
data = SwapEvent.ShowAlert(errorAlert),
onConsume = onDismiss,
)
} ?: consumedEvent(),
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
)
}
fun createDemoModeAlert(
uiState: SwapStateHolder,
onDismiss: () -> Unit,
isReverseSwapPossible: Boolean,
): SwapStateHolder {
return uiState.copy(
event = triggeredEvent(
data = SwapEvent.ShowAlert(AlertDemoModeUM(onDismiss)),
onConsume = onDismiss,
),
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
)
}
@Suppress("LongParameterList")
fun createAlert(
uiState: SwapStateHolder,
isPriceImpact: Boolean,
token: String,
provider: SwapProvider,
onDismiss: () -> Unit,
isReverseSwapPossible: Boolean,
): SwapStateHolder {
val slippage = provider.slippage?.let { "${it.parseBigDecimal(1)}$PERCENT" }
val combinedMessage = buildList {
when (provider.type) {
ExchangeProviderType.CEX -> {
if (slippage != null) {
add(
resourceReference(
id = R.string.swapping_alert_cex_description_with_slippage,
formatArgs = wrappedList(token, slippage),
),
)
} else {
add(resourceReference(R.string.swapping_alert_cex_description, wrappedList(token)))
}
}
ExchangeProviderType.DEX,
ExchangeProviderType.DEX_BRIDGE,
-> {
if (isPriceImpact) {
add(resourceReference(R.string.swapping_high_price_impact_description))
add(stringReference("\n\n"))
}
if (slippage != null) {
add(
resourceReference(
id = R.string.swapping_alert_dex_description_with_slippage,
formatArgs = wrappedList(slippage),
),
)
} else {
add(resourceReference(R.string.swapping_alert_dex_description, wrappedList(token)))
}
}
}
}
return uiState.copy(
event = triggeredEvent(
SwapEvent.ShowAlert(
SwapAlertUM.InformationAlert(
message = combinedReference(combinedMessage.toWrappedList()),
onConfirmClick = onDismiss,
),
),
onConsume = onDismiss,
),
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
)
}
fun addAlert(
uiState: SwapStateHolder,
message: TextReference = resourceReference(R.string.common_unknown_error),
onDismiss: () -> Unit = { clearAlert(uiState) },
): SwapStateHolder {
return uiState.copy(
event = triggeredEvent(
SwapEvent.ShowAlert(
SwapAlertUM.GenericError(onDismiss, message),
),
onConsume = onDismiss,
),
)
}
fun clearAlert(uiState: SwapStateHolder): SwapStateHolder = uiState.copy(event = consumedEvent())
fun addNotification(uiState: SwapStateHolder, message: TextReference?, onClick: () -> Unit): SwapStateHolder { fun addNotification(uiState: SwapStateHolder, message: TextReference?, onClick: () -> Unit): SwapStateHolder {
return uiState.copy( return uiState.copy(
notifications = notificationsFactory.getGeneralErrorStateNotifications( notifications = notificationsFactory.getGeneralErrorStateNotifications(

View file

@ -1,61 +0,0 @@
package com.tangem.feature.swap.ui
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import com.tangem.common.ui.alerts.models.AlertUM
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.event.EventEffect
import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.feature.swap.models.states.events.SwapEvent
import com.tangem.feature.swap.presentation.R
@Composable
internal fun SwapEventEffect(event: StateEvent<SwapEvent>) {
var alertConfig by remember { mutableStateOf<AlertUM?>(value = null) }
val keyboardController = LocalSoftwareKeyboardController.current
LaunchedEffect(key1 = alertConfig) {
keyboardController?.hide()
}
alertConfig?.let {
SwapAlert(state = it, onDismiss = { alertConfig = null })
}
EventEffect(
event = event,
onTrigger = { value ->
when (value) {
is SwapEvent.ShowAlert -> {
alertConfig = value.alert
}
}
},
)
}
@Composable
internal fun SwapAlert(state: AlertUM, onDismiss: () -> Unit) {
val confirmButton = DialogButtonUM(
title = state.confirmButtonText.resolveReference(),
onClick = {
state.onConfirmClick?.invoke()
onDismiss()
},
)
val dismissButton = DialogButtonUM(
title = stringResourceSafe(id = R.string.common_cancel),
onClick = onDismiss,
)
BasicDialog(
message = state.message.resolveReference(),
confirmButton = confirmButton,
onDismissDialog = onDismiss,
title = state.title?.resolveReference(),
dismissButton = dismissButton,
)
}

View file

@ -117,10 +117,6 @@ internal fun SwapScreenContent(
textAlign = TextAlign.Start, textAlign = TextAlign.Start,
) )
} }
SwapEventEffect(
event = state.event,
)
} }
} }

View file

@ -1,7 +1,6 @@
package com.tangem.features.yield.supply.impl.common package com.tangem.features.yield.supply.impl.common
import com.tangem.common.ui.alerts.TransactionErrorAlertConverter import com.tangem.common.ui.alerts.TransactionErrorDialogFactory
import com.tangem.common.ui.alerts.models.AlertDemoModeUM
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
@ -24,6 +23,7 @@ class YieldSupplyAlertFactory @Inject constructor(
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase, private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase, private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val transactionErrorDialogFactory: TransactionErrorDialogFactory,
) { ) {
fun getGenericErrorState(onFailedTxEmailClick: () -> Unit, popBack: () -> Unit = {}) { fun getGenericErrorState(onFailedTxEmailClick: () -> Unit, popBack: () -> Unit = {}) {
@ -41,35 +41,17 @@ class YieldSupplyAlertFactory @Inject constructor(
} }
fun getSendTransactionErrorState( fun getSendTransactionErrorState(
error: SendTransactionError?, error: SendTransactionError,
popBack: () -> Unit, popBack: () -> Unit,
onFailedTxEmailClick: (String) -> Unit, onFailedTxEmailClick: (String) -> Unit,
) { ) {
val transactionErrorAlertConverter = TransactionErrorAlertConverter( val errorDialog = transactionErrorDialogFactory.create(
error = error,
popBackStack = popBack, popBackStack = popBack,
onFailedTxEmailClick = onFailedTxEmailClick, onFailedTxEmailClick = onFailedTxEmailClick,
) ) ?: return
val errorAlert = error?.let { transactionErrorAlertConverter.convert(error) } ?: return uiMessageSender.send(errorDialog)
val onConfirmClick = errorAlert.onConfirmClick ?: return
uiMessageSender.send(
DialogMessage.Companion(
title = errorAlert.title,
message = errorAlert.message,
firstActionBuilder = {
EventMessageAction(
title = errorAlert.confirmButtonText,
onClick = onConfirmClick,
)
},
secondActionBuilder = if (errorAlert !is AlertDemoModeUM) {
{ cancelAction() }
} else {
null
},
),
)
} }
suspend fun onFailedTxEmailClick(userWallet: UserWallet, cryptoCurrency: CryptoCurrency?, errorMessage: String?) { suspend fun onFailedTxEmailClick(userWallet: UserWallet, cryptoCurrency: CryptoCurrency?, errorMessage: String?) {