Updated on 2026-08-14
This commit is contained in:
parent
adaa72f359
commit
94623dd8eb
14 changed files with 176 additions and 86 deletions
|
|
@ -5,18 +5,21 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.features.send.impl.presentation.analytics.SelectedCurrencyType
|
||||
import com.tangem.features.send.impl.presentation.analytics.SelectedFeeType
|
||||
import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents
|
||||
import com.tangem.features.send.impl.presentation.state.*
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiStateType
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
||||
import com.tangem.utils.Provider
|
||||
|
||||
internal class SendOnNextScreenAnalyticSender(
|
||||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) {
|
||||
fun send(prevScreen: SendUiStateType, state: SendUiState) {
|
||||
when (prevScreen) {
|
||||
SendUiStateType.Fee -> {
|
||||
val feeState = state.feeState ?: return
|
||||
val feeState = state.getFeeState(stateRouterProvider().isEditState) ?: return
|
||||
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content
|
||||
feeSelectorState?.selectedFee?.let { selectedFee ->
|
||||
val isCustomFeeEdited = feeState.fee?.amount?.value != feeSelectorState.fees.normal.amount.value
|
||||
|
|
@ -27,7 +30,8 @@ internal class SendOnNextScreenAnalyticSender(
|
|||
}
|
||||
}
|
||||
SendUiStateType.Amount -> {
|
||||
val isFiatSelected = state.amountState?.amountTextField?.isFiatValue ?: return
|
||||
val amountState = state.getAmountState(stateRouterProvider().isEditState) ?: return
|
||||
val isFiatSelected = amountState.amountTextField.isFiatValue
|
||||
val selectedCurrency = if (!isFiatSelected) {
|
||||
SelectedCurrencyType.Token
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.math.BigDecimal
|
|||
* @param feeStateFactory [FeeStateFactory]
|
||||
*/
|
||||
internal class SendEventStateFactory(
|
||||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val clickIntents: SendClickIntents,
|
||||
|
|
@ -50,7 +51,8 @@ internal class SendEventStateFactory(
|
|||
|
||||
fun getFeeUpdatedAlert(fee: TransactionFee, onConsume: () -> Unit, onFeeNotIncreased: () -> Unit): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val feeSelector = state.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return state
|
||||
val feeState = state.getFeeState(stateRouterProvider().isEditState)
|
||||
val feeSelector = feeState?.feeSelectorState as? FeeSelectorState.Content ?: return state
|
||||
val newFee = when (fee) {
|
||||
is TransactionFee.Single -> fee.normal
|
||||
is TransactionFee.Choosable -> {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.tangem.domain.wallets.usecase.ValidateWalletMemoUseCase
|
|||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.domain.AvailableWallet
|
||||
import com.tangem.features.send.impl.presentation.state.amount.SendAmountStateConverter
|
||||
import com.tangem.features.send.impl.presentation.state.common.SendSyncEditConverter
|
||||
import com.tangem.features.send.impl.presentation.state.confirm.SendConfirmStateConverter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
|
||||
import com.tangem.features.send.impl.presentation.state.fee.SendFeeStateConverter
|
||||
|
|
@ -31,6 +32,7 @@ import timber.log.Timber
|
|||
@Suppress("LongParameterList")
|
||||
internal class SendStateFactory(
|
||||
private val clickIntents: SendClickIntents,
|
||||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val userWalletProvider: Provider<UserWallet>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
|
|
@ -82,7 +84,9 @@ internal class SendStateFactory(
|
|||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
)
|
||||
}
|
||||
|
||||
private val sendSyncEditConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SendSyncEditConverter(currentStateProvider = currentStateProvider)
|
||||
}
|
||||
// region UI states
|
||||
fun getInitialState(): SendUiState = SendUiState(
|
||||
clickIntents = clickIntents,
|
||||
|
|
@ -117,6 +121,8 @@ internal class SendStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun syncEditStates(isFromEdit: Boolean) = sendSyncEditConverter.convert(isFromEdit)
|
||||
|
||||
fun getOnHideBalanceState(isBalanceHidden: Boolean): SendUiState {
|
||||
return currentStateProvider().copy(isBalanceHidden = isBalanceHidden)
|
||||
}
|
||||
|
|
@ -143,8 +149,10 @@ internal class SendStateFactory(
|
|||
|
||||
fun onRecipientAddressValueChange(value: String, isXAddress: Boolean = false): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val recipientState = state.recipientState ?: return state
|
||||
return state.copy(
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val recipientState = state.getRecipientState(isEditState) ?: return state
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
recipientState = recipientState.copy(
|
||||
addressTextField = recipientState.addressTextField.copy(value = value),
|
||||
memoTextField = recipientState.memoTextField?.copy(isEnabled = !isXAddress),
|
||||
|
|
@ -155,7 +163,8 @@ internal class SendStateFactory(
|
|||
fun getOnRecipientAddressValidState(value: String, isValidAddress: Boolean): SendUiState {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val state = currentStateProvider()
|
||||
val recipientState = state.recipientState ?: return state
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val recipientState = state.getRecipientState(isEditState) ?: return state
|
||||
|
||||
val isValidMemo = validateWalletMemoUseCase(
|
||||
memo = recipientState.memoTextField?.value.orEmpty(),
|
||||
|
|
@ -167,7 +176,8 @@ internal class SendStateFactory(
|
|||
val isAddressInWallet = cryptoCurrencyStatus.value.networkAddress?.availableAddresses
|
||||
?.any { it.value == value } ?: true
|
||||
|
||||
return state.copy(
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
recipientState = recipientState.copy(
|
||||
isPrimaryButtonEnabled = isValidMemo && isValidAddress && !isAddressInWallet,
|
||||
isValidating = false,
|
||||
|
|
@ -185,16 +195,20 @@ internal class SendStateFactory(
|
|||
|
||||
fun getOnRecipientAddressValidationStarted(): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val recipientState = state.recipientState ?: return state
|
||||
return state.copy(
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val recipientState = state.getRecipientState(isEditState) ?: return state
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
recipientState = recipientState.copy(isValidating = true),
|
||||
)
|
||||
}
|
||||
|
||||
fun getOnRecipientMemoValueChange(value: String): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val recipientState = state.recipientState ?: return state
|
||||
return state.copy(
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val recipientState = state.getRecipientState(isEditState) ?: return state
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
recipientState = recipientState.copy(
|
||||
memoTextField = recipientState.memoTextField?.copy(value = value),
|
||||
),
|
||||
|
|
@ -204,7 +218,8 @@ internal class SendStateFactory(
|
|||
fun getOnRecipientMemoValidState(value: String, isValidAddress: Boolean): SendUiState {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val state = currentStateProvider()
|
||||
val recipientState = state.recipientState ?: return state
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val recipientState = state.getRecipientState(isEditState) ?: return state
|
||||
|
||||
val isValidMemo = validateWalletMemoUseCase(
|
||||
memo = value,
|
||||
|
|
@ -216,7 +231,8 @@ internal class SendStateFactory(
|
|||
val isAddressInWallet = cryptoCurrencyStatus.value.networkAddress?.availableAddresses
|
||||
?.any { it.value == value } ?: true
|
||||
|
||||
return state.copy(
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
recipientState = recipientState.copy(
|
||||
isPrimaryButtonEnabled = isValidMemo && isValidAddress && !isAddressInWallet,
|
||||
isValidating = false,
|
||||
|
|
@ -230,8 +246,10 @@ internal class SendStateFactory(
|
|||
|
||||
fun getOnXAddressMemoState(): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val recipientState = state.recipientState ?: return state
|
||||
return state.copy(
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val recipientState = state.getRecipientState(isEditState) ?: return state
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
recipientState = recipientState.copy(
|
||||
memoTextField = recipientState.memoTextField?.copy(
|
||||
value = "",
|
||||
|
|
@ -243,9 +261,11 @@ internal class SendStateFactory(
|
|||
|
||||
fun getHiddenRecentListState(isAddressInWallet: Boolean, isValidAddress: Boolean): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val recipientState = state.recipientState ?: return state
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val recipientState = state.getRecipientState(isEditState) ?: return state
|
||||
val isNotValid = isAddressInWallet || !isValidAddress
|
||||
return state.copy(
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
recipientState = recipientState.copy(
|
||||
recent = recipientState.recent.map { recent ->
|
||||
recent.copy(isVisible = isNotValid && (recent.isLoading || recent.title != TextReference.EMPTY))
|
||||
|
|
@ -305,7 +325,7 @@ internal class SendStateFactory(
|
|||
|
||||
private fun isPrimaryButtonEnabled(state: SendUiState, notifications: ImmutableList<SendNotification>): Boolean {
|
||||
val sendState = state.sendState ?: return false
|
||||
val feeState = state.feeState ?: return false
|
||||
val feeState = state.getFeeState(stateRouterProvider().isEditState) ?: return false
|
||||
val hasErrorNotifications = notifications.any { it is SendNotification.Error }
|
||||
return !hasErrorNotifications && !sendState.isSending && feeState.feeSelectorState is FeeSelectorState.Content
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.amount
|
|||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldChangeConverter
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldMaxAmountConverter
|
||||
import com.tangem.utils.Provider
|
||||
|
|
@ -10,18 +11,21 @@ import com.tangem.utils.Provider
|
|||
* Factory to produce amount state for [SendUiState]
|
||||
*/
|
||||
internal class AmountStateFactory(
|
||||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
) {
|
||||
|
||||
private val amountFieldChangeConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SendAmountFieldChangeConverter(
|
||||
stateRouterProvider = stateRouterProvider,
|
||||
currentStateProvider = currentStateProvider,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
)
|
||||
}
|
||||
private val amountFieldMaxAmountConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SendAmountFieldMaxAmountConverter(
|
||||
stateRouterProvider = stateRouterProvider,
|
||||
currentStateProvider = currentStateProvider,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
)
|
||||
|
|
@ -29,12 +33,14 @@ internal class AmountStateFactory(
|
|||
|
||||
private val amountCurrencyConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SendAmountCurrencyConverter(
|
||||
stateRouterProvider = stateRouterProvider,
|
||||
currentStateProvider = currentStateProvider,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
)
|
||||
}
|
||||
private val amountPasteConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SendAmountPastedTriggerDismissConverter(
|
||||
stateRouterProvider = stateRouterProvider,
|
||||
currentStateProvider = currentStateProvider,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,20 @@ package com.tangem.features.send.impl.presentation.state.amount
|
|||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.isNullOrZero
|
||||
|
||||
internal class SendAmountCurrencyConverter(
|
||||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
) : Converter<Boolean, SendUiState> {
|
||||
override fun convert(value: Boolean): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val amountState = state.amountState ?: return state
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val amountState = state.getAmountState(isEditState) ?: return state
|
||||
val amountTextField = amountState.amountTextField
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
|
||||
|
|
@ -20,7 +23,8 @@ internal class SendAmountCurrencyConverter(
|
|||
return if (amountTextField.isFiatValue == value && !isValidFiatRate) {
|
||||
state
|
||||
} else {
|
||||
return state.copy(
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
amountState = amountState.copy(
|
||||
amountTextField = amountTextField.copy(
|
||||
isFiatValue = value,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
package com.tangem.features.send.impl.presentation.state.amount
|
||||
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class SendAmountPastedTriggerDismissConverter(
|
||||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
) : Converter<Boolean, SendUiState> {
|
||||
override fun convert(value: Boolean): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val amountState = state.amountState ?: return state
|
||||
return state.copy(
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val amountState = state.getAmountState(isEditState) ?: return state
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
amountState = amountState.copy(
|
||||
amountTextField = amountState.amountTextField.copy(
|
||||
isValuePasted = false,
|
||||
|
|
|
|||
|
|
@ -49,10 +49,13 @@ internal class SendNotificationFactory(
|
|||
.filter { it.type == SendUiStateType.Send }
|
||||
.map {
|
||||
val state = currentStateProvider()
|
||||
val sendState = state.sendState ?: return@map persistentListOf()
|
||||
val feeState = state.feeState ?: return@map persistentListOf()
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val balance = cryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO
|
||||
val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO
|
||||
val sendState = state.sendState ?: return@map persistentListOf()
|
||||
val feeState = state.getFeeState(isEditState) ?: return@map persistentListOf()
|
||||
val amountState = state.getAmountState(isEditState) ?: return@map persistentListOf()
|
||||
|
||||
val amountValue = amountState.amountTextField.cryptoAmount.value ?: BigDecimal.ZERO
|
||||
val feeValue = feeState.fee?.amount?.value ?: BigDecimal.ZERO
|
||||
val isFeeCoverage = checkFeeCoverage(
|
||||
isSubtractAvailable = isSubtractAvailableProvider(),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.core.ui.utils.parseBigDecimal
|
|||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.common.extensions.minimalAmount
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
|
@ -73,8 +72,7 @@ internal fun calculateSubtractedAmount(
|
|||
/**
|
||||
* Check if custom fee is too low
|
||||
*/
|
||||
internal fun checkIfFeeTooLow(state: SendUiState): Boolean {
|
||||
val feeSelectorState = state.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return false
|
||||
internal fun checkIfFeeTooLow(feeSelectorState: FeeSelectorState.Content): Boolean {
|
||||
val multipleFees = feeSelectorState.fees as? TransactionFee.Choosable ?: return false
|
||||
val minimumValue = multipleFees.minimum.amount.value ?: return false
|
||||
val customAmount = feeSelectorState.customValues.firstOrNull() ?: return false
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
package com.tangem.features.send.impl.presentation.state.fee
|
||||
|
||||
import com.tangem.features.send.impl.presentation.state.SendNotification
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiStateType
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.state.*
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -19,10 +16,10 @@ internal class FeeNotificationFactory(
|
|||
) {
|
||||
|
||||
fun create() = stateRouterProvider().currentState
|
||||
.filter { it.type == SendUiStateType.Fee }
|
||||
.filter { it.type == SendUiStateType.Fee || it.type == SendUiStateType.EditFee }
|
||||
.map {
|
||||
val state = currentStateProvider()
|
||||
val feeState = state.feeState ?: return@map persistentListOf()
|
||||
val feeState = state.getFeeState(stateRouterProvider().isEditState) ?: return@map persistentListOf()
|
||||
buildList {
|
||||
addFeeUnreachableNotification(feeState.feeSelectorState)
|
||||
}.toImmutableList()
|
||||
|
|
|
|||
|
|
@ -7,9 +7,7 @@ import com.tangem.core.ui.utils.parseToBigDecimal
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
|
||||
import com.tangem.features.send.impl.presentation.state.SendNotification
|
||||
import com.tangem.features.send.impl.presentation.state.SendStates
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.*
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -20,6 +18,7 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
*/
|
||||
internal class FeeStateFactory(
|
||||
private val clickIntents: SendClickIntents,
|
||||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val feeCryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
|
|
@ -43,8 +42,10 @@ internal class FeeStateFactory(
|
|||
|
||||
fun onFeeOnLoadingState(): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val feeState = state.feeState ?: return state
|
||||
return state.copy(
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val feeState = state.getFeeState(isEditState) ?: return state
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
sendState = state.sendState?.copy(
|
||||
isPrimaryButtonEnabled = false,
|
||||
),
|
||||
|
|
@ -62,7 +63,8 @@ internal class FeeStateFactory(
|
|||
|
||||
fun onFeeOnLoadedState(fees: TransactionFee): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val feeState = state.feeState ?: return state
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val feeState = state.getFeeState(isEditState) ?: return state
|
||||
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content
|
||||
|
||||
val isCustomWasSelected = if (feeState.isCustomSelected) {
|
||||
|
|
@ -79,7 +81,8 @@ internal class FeeStateFactory(
|
|||
)
|
||||
|
||||
val fee = feeConverter.convert(updatedFeeSelectorState)
|
||||
return state.copy(
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
sendState = state.sendState?.copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
),
|
||||
|
|
@ -93,8 +96,10 @@ internal class FeeStateFactory(
|
|||
|
||||
fun onFeeOnErrorState(): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
return state.copy(
|
||||
feeState = state.feeState?.copy(
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
feeState = state.getFeeState(isEditState)?.copy(
|
||||
feeSelectorState = FeeSelectorState.Error,
|
||||
),
|
||||
sendState = state.sendState?.copy(
|
||||
|
|
@ -105,13 +110,15 @@ internal class FeeStateFactory(
|
|||
|
||||
fun onFeeSelectedState(feeType: FeeType): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val feeState = state.feeState ?: return state
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val feeState = state.getFeeState(isEditState) ?: return state
|
||||
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state
|
||||
|
||||
val updatedFeeSelectorState = feeSelectorState.copy(selectedFee = feeType)
|
||||
val fee = feeConverter.convert(updatedFeeSelectorState)
|
||||
val isCustomFeeWasSelected = feeState.isCustomSelected || updatedFeeSelectorState.selectedFee == FeeType.Custom
|
||||
return state.copy(
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
feeState = feeState.copy(
|
||||
fee = fee,
|
||||
isCustomSelected = isCustomFeeWasSelected,
|
||||
|
|
@ -122,12 +129,14 @@ internal class FeeStateFactory(
|
|||
|
||||
fun onCustomFeeValueChange(index: Int, value: String): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val feeState = state.feeState ?: return state
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val feeState = state.getFeeState(isEditState) ?: return state
|
||||
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state
|
||||
val updatedFeeSelectorState = customFeeFieldConverter.onValueChange(feeSelectorState, index, value)
|
||||
|
||||
val fee = feeConverter.convert(updatedFeeSelectorState)
|
||||
return state.copy(
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
feeState = feeState.copy(
|
||||
feeSelectorState = updatedFeeSelectorState,
|
||||
fee = fee,
|
||||
|
|
@ -137,10 +146,13 @@ internal class FeeStateFactory(
|
|||
|
||||
fun getFeeNotificationState(notifications: ImmutableList<SendNotification>): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
return state.copy(
|
||||
feeState = state.feeState?.copy(
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val feeState = state.getFeeState(isEditState) ?: return state
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
feeState = feeState.copy(
|
||||
notifications = notifications,
|
||||
isPrimaryButtonEnabled = isPrimaryButtonEnabled(state.feeState, notifications),
|
||||
isPrimaryButtonEnabled = isPrimaryButtonEnabled(feeState, notifications),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,18 +8,21 @@ import com.tangem.core.ui.utils.parseBigDecimal
|
|||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
||||
internal class SendAmountFieldChangeConverter(
|
||||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
) : Converter<String, SendUiState> {
|
||||
override fun convert(value: String): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val amountState = state.amountState ?: return state
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val amountState = state.getAmountState(isEditState) ?: return state
|
||||
val amountTextField = amountState.amountTextField
|
||||
|
||||
if (value.isEmpty()) return state.emptyState()
|
||||
|
|
@ -35,7 +38,8 @@ internal class SendAmountFieldChangeConverter(
|
|||
val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue
|
||||
val isExceedBalance = checkValue.checkExceedBalance(amountTextField)
|
||||
val isZero = if (amountTextField.isFiatValue) decimalFiatValue.isZero() else decimalCryptoValue.isZero()
|
||||
return state.copy(
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
amountState = amountState.copy(
|
||||
isPrimaryButtonEnabled = !isExceedBalance && !isZero,
|
||||
amountTextField = amountTextField.copy(
|
||||
|
|
@ -75,9 +79,11 @@ internal class SendAmountFieldChangeConverter(
|
|||
}
|
||||
|
||||
private fun SendUiState.emptyState(): SendUiState {
|
||||
if (amountState == null) return this
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val amountState = getAmountState(isEditState) ?: return this
|
||||
val amountTextField = amountState.amountTextField
|
||||
return copy(
|
||||
return copyWrapped(
|
||||
isEditState = isEditState,
|
||||
amountState = amountState.copy(
|
||||
isPrimaryButtonEnabled = false,
|
||||
amountTextField = amountTextField.copy(
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.isNullOrZero
|
||||
|
||||
internal class SendAmountFieldMaxAmountConverter(
|
||||
private val stateRouterProvider: Provider<StateRouter>,
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
) : Converter<Unit, SendUiState> {
|
||||
|
|
@ -18,7 +20,8 @@ internal class SendAmountFieldMaxAmountConverter(
|
|||
override fun convert(value: Unit): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val amountState = state.amountState ?: return state
|
||||
val isEditState = stateRouterProvider().isEditState
|
||||
val amountState = state.getAmountState(isEditState) ?: return state
|
||||
val amountTextField = amountState.amountTextField
|
||||
|
||||
val cryptoDecimals = amountTextField.cryptoAmount.decimals
|
||||
|
|
@ -31,7 +34,8 @@ internal class SendAmountFieldMaxAmountConverter(
|
|||
val isDoneActionEnabled = !decimalCryptoValue.isNullOrZero()
|
||||
val cryptoValue = decimalCryptoValue?.parseBigDecimal(cryptoDecimals).orEmpty()
|
||||
val fiatValue = decimalFiatValue?.parseBigDecimal(fiatDecimals).orEmpty()
|
||||
return state.copy(
|
||||
return state.copyWrapped(
|
||||
isEditState = isEditState,
|
||||
amountState = amountState.copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
amountTextField = amountTextField.copy(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ package com.tangem.features.send.impl.presentation.ui
|
|||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -50,7 +53,11 @@ internal fun SendNavigationButtons(
|
|||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
) {
|
||||
SendingText(uiState = uiState, isVisible = isSendingState)
|
||||
SendingText(
|
||||
uiState = uiState,
|
||||
isEditState = currentState.isFromConfirmation,
|
||||
isVisible = isSendingState,
|
||||
)
|
||||
SendDoneButtons(
|
||||
txUrl = sendState.txUrl,
|
||||
onExploreClick = uiState.clickIntents::onExploreClick,
|
||||
|
|
@ -130,7 +137,12 @@ private fun SendNavigationButton(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun SendingText(uiState: SendUiState, isVisible: Boolean, modifier: Modifier = Modifier) {
|
||||
private fun SendingText(
|
||||
uiState: SendUiState,
|
||||
isEditState: Boolean,
|
||||
isVisible: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var isVisibleProxy by remember { mutableStateOf(isVisible) }
|
||||
|
||||
// text appearance delay for smooth screen transitions
|
||||
|
|
@ -148,8 +160,8 @@ private fun SendingText(uiState: SendUiState, isVisible: Boolean, modifier: Modi
|
|||
exit = slideOutVertically().plus(fadeOut()),
|
||||
label = "Animate show sending state text",
|
||||
) {
|
||||
val amountState = uiState.amountState
|
||||
val feeState = uiState.feeState
|
||||
val amountState = uiState.getAmountState(isEditState)
|
||||
val feeState = uiState.getFeeState(isEditState)
|
||||
val fiatRate = feeState?.rate
|
||||
val fiatAmount = amountState?.amountTextField?.fiatAmount
|
||||
val feeFiat = fiatRate?.let { feeState.fee?.amount?.value?.multiply(it) }
|
||||
|
|
@ -230,11 +242,11 @@ private fun getButtonData(
|
|||
SendUiStateType.Amount,
|
||||
SendUiStateType.Recipient,
|
||||
SendUiStateType.Fee,
|
||||
-> if (currentState.isFromConfirmation) {
|
||||
R.string.common_continue to uiState.clickIntents::onNextClick
|
||||
} else {
|
||||
R.string.common_next to uiState.clickIntents::onNextClick
|
||||
}
|
||||
-> R.string.common_next to { uiState.clickIntents.onNextClick() }
|
||||
SendUiStateType.EditFee,
|
||||
SendUiStateType.EditAmount,
|
||||
SendUiStateType.EditRecipient,
|
||||
-> R.string.common_continue to { uiState.clickIntents.onNextClick(isFromEdit = true) }
|
||||
SendUiStateType.Send -> when {
|
||||
isSuccess -> R.string.common_close
|
||||
isSending -> R.string.send_sending
|
||||
|
|
@ -245,10 +257,13 @@ private fun getButtonData(
|
|||
|
||||
private fun isButtonEnabled(currentState: SendUiCurrentScreen, uiState: SendUiState): Boolean {
|
||||
return when (currentState.type) {
|
||||
SendUiStateType.Amount -> uiState.amountState?.isPrimaryButtonEnabled ?: false
|
||||
SendUiStateType.Recipient -> uiState.recipientState?.isPrimaryButtonEnabled ?: false
|
||||
SendUiStateType.Fee -> uiState.feeState?.isPrimaryButtonEnabled ?: false
|
||||
SendUiStateType.Send -> uiState.sendState?.isPrimaryButtonEnabled ?: false
|
||||
SendUiStateType.Amount -> uiState.amountState?.isPrimaryButtonEnabled
|
||||
SendUiStateType.Recipient -> uiState.recipientState?.isPrimaryButtonEnabled
|
||||
SendUiStateType.Fee -> uiState.feeState?.isPrimaryButtonEnabled
|
||||
SendUiStateType.Send -> uiState.sendState?.isPrimaryButtonEnabled
|
||||
SendUiStateType.EditAmount -> uiState.editAmountState?.isPrimaryButtonEnabled
|
||||
SendUiStateType.EditRecipient -> uiState.editRecipientState?.isPrimaryButtonEnabled
|
||||
SendUiStateType.EditFee -> uiState.editFeeState?.isPrimaryButtonEnabled
|
||||
else -> true
|
||||
}
|
||||
} ?: false
|
||||
}
|
||||
|
|
@ -122,6 +122,7 @@ internal class SendViewModel @Inject constructor(
|
|||
|
||||
private val stateFactory = SendStateFactory(
|
||||
clickIntents = this,
|
||||
stateRouterProvider = Provider { stateRouter },
|
||||
currentStateProvider = Provider { uiState },
|
||||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
|
|
@ -132,12 +133,14 @@ internal class SendViewModel @Inject constructor(
|
|||
)
|
||||
|
||||
private val amountStateFactory = AmountStateFactory(
|
||||
stateRouterProvider = Provider { stateRouter },
|
||||
currentStateProvider = Provider { uiState },
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
)
|
||||
|
||||
private val feeStateFactory = FeeStateFactory(
|
||||
clickIntents = this,
|
||||
stateRouterProvider = Provider { stateRouter },
|
||||
currentStateProvider = Provider { uiState },
|
||||
feeCryptoCurrencyStatusProvider = Provider { feeCryptoCurrencyStatus },
|
||||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
|
|
@ -146,6 +149,7 @@ internal class SendViewModel @Inject constructor(
|
|||
|
||||
private val eventStateFactory = SendEventStateFactory(
|
||||
clickIntents = this,
|
||||
stateRouterProvider = Provider { stateRouter },
|
||||
currentStateProvider = Provider { uiState },
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
feeStateFactory = feeStateFactory,
|
||||
|
|
@ -171,7 +175,10 @@ internal class SendViewModel @Inject constructor(
|
|||
)
|
||||
|
||||
private val sendOnNextScreenAnalyticSender by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SendOnNextScreenAnalyticSender(analyticsEventHandler)
|
||||
SendOnNextScreenAnalyticSender(
|
||||
stateRouterProvider = Provider { stateRouter },
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
)
|
||||
}
|
||||
|
||||
// todo convert to StateFlow
|
||||
|
|
@ -454,7 +461,9 @@ internal class SendViewModel @Inject constructor(
|
|||
stateRouter.currentState
|
||||
.onEach {
|
||||
when (it.type) {
|
||||
SendUiStateType.Fee -> loadFee()
|
||||
SendUiStateType.Fee,
|
||||
SendUiStateType.EditFee,
|
||||
-> loadFee()
|
||||
SendUiStateType.Send -> sendIdleTimer = SystemClock.elapsedRealtime()
|
||||
else -> Unit
|
||||
}
|
||||
|
|
@ -489,16 +498,17 @@ internal class SendViewModel @Inject constructor(
|
|||
stateRouter.onBackClick(isSuccess = uiState.sendState?.isSuccess == true)
|
||||
}
|
||||
|
||||
override fun onNextClick() {
|
||||
override fun onNextClick(isFromEdit: Boolean) {
|
||||
val currentState = stateRouter.currentState.value
|
||||
uiState = stateFactory.syncEditStates(isFromEdit = isFromEdit)
|
||||
sendOnNextScreenAnalyticSender.send(currentState.type, uiState)
|
||||
when (currentState.type) {
|
||||
SendUiStateType.Fee -> {
|
||||
if (onFeeNext()) return
|
||||
}
|
||||
SendUiStateType.Amount -> {
|
||||
loadFee()
|
||||
}
|
||||
SendUiStateType.Fee,
|
||||
SendUiStateType.EditFee,
|
||||
-> if (onFeeNext()) return
|
||||
SendUiStateType.Amount,
|
||||
SendUiStateType.EditAmount,
|
||||
-> loadFee()
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
|
|
@ -523,13 +533,14 @@ internal class SendViewModel @Inject constructor(
|
|||
innerRouter.openTokenDetails(userWalletId, currency)
|
||||
|
||||
private fun onFeeNext(): Boolean {
|
||||
if (checkIfFeeTooLow(uiState)) {
|
||||
val feeState = uiState.getFeeState(stateRouter.isEditState)
|
||||
val feeSelectorState = feeState?.feeSelectorState as? FeeSelectorState.Content ?: return false
|
||||
if (checkIfFeeTooLow(feeSelectorState)) {
|
||||
uiState = eventStateFactory.getFeeTooLowAlert(
|
||||
onConsume = { uiState = eventStateFactory.onConsumeEventState() },
|
||||
)
|
||||
return true
|
||||
}
|
||||
val feeSelectorState = uiState.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return false
|
||||
return checkIfFeeTooHigh(
|
||||
feeSelectorState = feeSelectorState,
|
||||
onShow = { diff ->
|
||||
|
|
@ -706,8 +717,9 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun callFeeUseCase(): Either<GetFeeError, TransactionFee>? {
|
||||
val amountState = uiState.amountState ?: return null
|
||||
val recipientState = uiState.recipientState ?: return null
|
||||
val isFromConfirmation = stateRouter.currentState.value.isFromConfirmation
|
||||
val amountState = uiState.getAmountState(isFromConfirmation) ?: return null
|
||||
val recipientState = uiState.getRecipientState(isFromConfirmation) ?: return null
|
||||
val amount = amountState.amountTextField.cryptoAmount.value ?: return null
|
||||
|
||||
return feeCryptoCurrencyStatus?.let { feeCurrencyStatus ->
|
||||
|
|
@ -736,12 +748,14 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun showAmount() {
|
||||
uiState = stateFactory.syncEditStates(isFromEdit = false)
|
||||
stateRouter.showAmount(isFromConfirmation = true)
|
||||
setNeverToShowTapHelp()
|
||||
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Amount))
|
||||
}
|
||||
|
||||
override fun showRecipient() {
|
||||
uiState = stateFactory.syncEditStates(isFromEdit = false)
|
||||
stateRouter.showRecipient(isFromConfirmation = true)
|
||||
uiState = stateFactory.getHiddenTapHelpState()
|
||||
setNeverToShowTapHelp()
|
||||
|
|
@ -749,6 +763,7 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun showFee() {
|
||||
uiState = stateFactory.syncEditStates(isFromEdit = false)
|
||||
stateRouter.showFee(isFromConfirmation = true)
|
||||
setNeverToShowTapHelp()
|
||||
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue