Updated on 2026-08-14
This commit is contained in:
commit
901b95dc68
20 changed files with 360 additions and 138 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,58 @@ internal data class SendUiState(
|
|||
val recipientState: SendStates.RecipientState? = null,
|
||||
val feeState: SendStates.FeeState? = null,
|
||||
val sendState: SendStates.SendState? = null,
|
||||
val editAmountState: SendStates.AmountState? = null,
|
||||
val editRecipientState: SendStates.RecipientState? = null,
|
||||
val editFeeState: SendStates.FeeState? = null,
|
||||
val isBalanceHidden: Boolean,
|
||||
val event: StateEvent<SendEvent>,
|
||||
)
|
||||
) {
|
||||
|
||||
fun getAmountState(isEditState: Boolean): SendStates.AmountState? {
|
||||
return if (isEditState) {
|
||||
editAmountState
|
||||
} else {
|
||||
amountState
|
||||
}
|
||||
}
|
||||
|
||||
fun getRecipientState(isEditState: Boolean): SendStates.RecipientState? {
|
||||
return if (isEditState) {
|
||||
editRecipientState
|
||||
} else {
|
||||
recipientState
|
||||
}
|
||||
}
|
||||
|
||||
fun getFeeState(isEditState: Boolean): SendStates.FeeState? {
|
||||
return if (isEditState) {
|
||||
editFeeState
|
||||
} else {
|
||||
feeState
|
||||
}
|
||||
}
|
||||
|
||||
fun copyWrapped(
|
||||
isEditState: Boolean,
|
||||
amountState: SendStates.AmountState? = this.amountState,
|
||||
feeState: SendStates.FeeState? = this.feeState,
|
||||
recipientState: SendStates.RecipientState? = this.recipientState,
|
||||
sendState: SendStates.SendState? = this.sendState,
|
||||
): SendUiState = if (isEditState) {
|
||||
copy(
|
||||
editAmountState = amountState,
|
||||
editFeeState = feeState,
|
||||
editRecipientState = recipientState,
|
||||
)
|
||||
} else {
|
||||
copy(
|
||||
amountState = amountState,
|
||||
feeState = feeState,
|
||||
recipientState = recipientState,
|
||||
sendState = sendState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
internal sealed class SendStates {
|
||||
|
|
@ -105,6 +154,9 @@ enum class SendUiStateType {
|
|||
None,
|
||||
Recipient,
|
||||
Amount,
|
||||
Send,
|
||||
Fee,
|
||||
Send,
|
||||
EditAmount,
|
||||
EditRecipient,
|
||||
EditFee,
|
||||
}
|
||||
|
|
@ -18,6 +18,9 @@ internal class StateRouter(
|
|||
val currentState: StateFlow<SendUiCurrentScreen>
|
||||
get() = mutableCurrentState
|
||||
|
||||
val isEditState: Boolean
|
||||
get() = currentState.value.isFromConfirmation
|
||||
|
||||
fun clear() {
|
||||
mutableCurrentState.update { getInitState() }
|
||||
}
|
||||
|
|
@ -35,21 +38,29 @@ internal class StateRouter(
|
|||
else -> popBackStack()
|
||||
}
|
||||
else -> when (type) {
|
||||
SendUiStateType.Amount -> continueToSend(::showRecipient)
|
||||
SendUiStateType.Amount -> showRecipient()
|
||||
SendUiStateType.Fee -> showSend()
|
||||
SendUiStateType.Send -> continueToSend(::showAmount)
|
||||
else -> continueToSend(::popBackStack)
|
||||
SendUiStateType.Send -> showAmount()
|
||||
SendUiStateType.Recipient -> popBackStack()
|
||||
SendUiStateType.EditAmount -> showSend()
|
||||
SendUiStateType.EditRecipient -> showSend()
|
||||
SendUiStateType.EditFee -> showSend()
|
||||
else -> popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onNextClick() {
|
||||
when (currentState.value.type) {
|
||||
SendUiStateType.Recipient -> continueToSend(::showAmount)
|
||||
SendUiStateType.Recipient -> showAmount()
|
||||
SendUiStateType.Amount,
|
||||
SendUiStateType.Fee,
|
||||
SendUiStateType.EditAmount,
|
||||
SendUiStateType.EditRecipient,
|
||||
SendUiStateType.EditFee,
|
||||
-> showSend()
|
||||
SendUiStateType.Send -> onBackClick()
|
||||
|
||||
else -> popBackStack()
|
||||
}
|
||||
}
|
||||
|
|
@ -67,17 +78,35 @@ internal class StateRouter(
|
|||
|
||||
fun showAmount(isFromConfirmation: Boolean = false) {
|
||||
analyticsEventsHandler.send(SendAnalyticEvents.AmountScreenOpened)
|
||||
mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Amount, isFromConfirmation) }
|
||||
mutableCurrentState.update {
|
||||
if (isFromConfirmation) {
|
||||
SendUiCurrentScreen(SendUiStateType.EditAmount, true)
|
||||
} else {
|
||||
SendUiCurrentScreen(SendUiStateType.Amount, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showRecipient(isFromConfirmation: Boolean = false) {
|
||||
analyticsEventsHandler.send(SendAnalyticEvents.AddressScreenOpened)
|
||||
mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Recipient, isFromConfirmation) }
|
||||
mutableCurrentState.update {
|
||||
if (isFromConfirmation) {
|
||||
SendUiCurrentScreen(SendUiStateType.EditRecipient, true)
|
||||
} else {
|
||||
SendUiCurrentScreen(SendUiStateType.Recipient, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showFee(isFromConfirmation: Boolean = false) {
|
||||
analyticsEventsHandler.send(SendAnalyticEvents.FeeScreenOpened)
|
||||
mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Fee, isFromConfirmation) }
|
||||
mutableCurrentState.update {
|
||||
if (isFromConfirmation) {
|
||||
SendUiCurrentScreen(SendUiStateType.EditFee, true)
|
||||
} else {
|
||||
SendUiCurrentScreen(SendUiStateType.Fee, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showSend() {
|
||||
|
|
@ -85,10 +114,6 @@ internal class StateRouter(
|
|||
mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Send, isFromConfirmation = false) }
|
||||
}
|
||||
|
||||
private fun continueToSend(show: () -> Unit) {
|
||||
if (currentState.value.isFromConfirmation) showSend() else show()
|
||||
}
|
||||
|
||||
private fun getInitState() = if (isEditingDisabled) {
|
||||
SendUiCurrentScreen(
|
||||
type = SendUiStateType.None,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.features.send.impl.presentation.state.common
|
||||
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class SendSyncEditConverter(
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
) : Converter<Boolean, SendUiState> {
|
||||
override fun convert(value: Boolean): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
return if (value) {
|
||||
state.copy(
|
||||
amountState = state.editAmountState,
|
||||
feeState = state.editFeeState,
|
||||
recipientState = state.editRecipientState,
|
||||
)
|
||||
} else {
|
||||
state.copy(
|
||||
editAmountState = state.amountState,
|
||||
editRecipientState = state.recipientState,
|
||||
editFeeState = state.feeState,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ internal object SendClickIntentsStub : SendClickIntents {
|
|||
|
||||
override fun onBackClick() {}
|
||||
|
||||
override fun onNextClick() {}
|
||||
override fun onNextClick(isFromEdit: Boolean) {}
|
||||
|
||||
override fun onPrevClick() {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
package com.tangem.features.send.impl.presentation.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
|
|
@ -23,12 +26,13 @@ import com.tangem.features.send.impl.presentation.ui.fee.SendSpeedAndFeeContent
|
|||
import com.tangem.features.send.impl.presentation.ui.recipient.SendRecipientContent
|
||||
import com.tangem.features.send.impl.presentation.ui.send.SendContent
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.withIndex
|
||||
|
||||
@Composable
|
||||
internal fun SendScreen(uiState: SendUiState, currentState: SendUiCurrentScreen) {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val sendState = uiState.sendState ?: return
|
||||
BackHandler { uiState.clickIntents.onBackClick() }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -38,38 +42,9 @@ internal fun SendScreen(uiState: SendUiState, currentState: SendUiCurrentScreen)
|
|||
.background(color = TangemTheme.colors.background.tertiary),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
val titleRes = when (currentState.type) {
|
||||
SendUiStateType.Amount -> resourceReference(R.string.send_amount_label)
|
||||
SendUiStateType.Recipient -> resourceReference(R.string.send_recipient_label)
|
||||
SendUiStateType.Fee -> resourceReference(R.string.common_fee_selector_title)
|
||||
SendUiStateType.Send -> if (!sendState.isSuccess) {
|
||||
resourceReference(R.string.send_summary_title, wrappedList(uiState.cryptoCurrencyName))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
val isSending = currentState.type == SendUiStateType.Send && !uiState.sendState.isSuccess
|
||||
val subtitleRes = if (isSending) {
|
||||
uiState.amountState?.walletName
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val iconRes = if (currentState.type == SendUiStateType.Recipient) {
|
||||
R.drawable.ic_qrcode_scan_24
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
AppBarWithBackButtonAndIcon(
|
||||
text = titleRes?.resolveReference(),
|
||||
subtitle = subtitleRes,
|
||||
onBackClick = uiState.clickIntents::popBackStack,
|
||||
onIconClick = uiState.clickIntents::onQrCodeScanClick,
|
||||
backIconRes = R.drawable.ic_close_24,
|
||||
iconRes = iconRes,
|
||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||
modifier = Modifier.height(TangemTheme.dimens.size56),
|
||||
SendAppBar(
|
||||
uiState = uiState,
|
||||
currentState = currentState,
|
||||
)
|
||||
SendScreenContent(
|
||||
uiState = uiState,
|
||||
|
|
@ -88,6 +63,50 @@ internal fun SendScreen(uiState: SendUiState, currentState: SendUiCurrentScreen)
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SendAppBar(uiState: SendUiState, currentState: SendUiCurrentScreen) {
|
||||
val (titleRes, subtitleRes) = when (currentState.type) {
|
||||
SendUiStateType.Amount,
|
||||
SendUiStateType.EditAmount,
|
||||
-> resourceReference(R.string.send_amount_label) to null
|
||||
SendUiStateType.Recipient,
|
||||
SendUiStateType.EditRecipient,
|
||||
-> resourceReference(R.string.send_recipient_label) to null
|
||||
SendUiStateType.Fee,
|
||||
SendUiStateType.EditFee,
|
||||
-> resourceReference(R.string.common_fee_selector_title) to null
|
||||
SendUiStateType.Send -> if (uiState.sendState?.isSuccess == false) {
|
||||
resourceReference(R.string.send_summary_title, wrappedList(uiState.cryptoCurrencyName)) to
|
||||
uiState.amountState?.walletName
|
||||
} else {
|
||||
null to null
|
||||
}
|
||||
else -> null to null
|
||||
}
|
||||
val iconRes = if (currentState.type == SendUiStateType.Recipient) {
|
||||
R.drawable.ic_qrcode_scan_24
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val (backIcon, backClick) = when (currentState.type) {
|
||||
SendUiStateType.EditAmount,
|
||||
SendUiStateType.EditFee,
|
||||
SendUiStateType.EditRecipient,
|
||||
-> R.drawable.ic_back_24 to uiState.clickIntents::onBackClick
|
||||
else -> R.drawable.ic_close_24 to uiState.clickIntents::popBackStack
|
||||
}
|
||||
AppBarWithBackButtonAndIcon(
|
||||
text = titleRes?.resolveReference(),
|
||||
subtitle = subtitleRes,
|
||||
onBackClick = backClick,
|
||||
onIconClick = uiState.clickIntents::onQrCodeScanClick,
|
||||
backIconRes = backIcon,
|
||||
iconRes = iconRes,
|
||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||
modifier = Modifier.height(TangemTheme.dimens.size56),
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
private fun SendScreenContent(uiState: SendUiState, currentState: SendUiCurrentScreen, modifier: Modifier = Modifier) {
|
||||
|
|
@ -135,13 +154,23 @@ private fun SendScreenContent(uiState: SendUiState, currentState: SendUiCurrentS
|
|||
isBalanceHiding = uiState.isBalanceHidden,
|
||||
clickIntents = uiState.clickIntents,
|
||||
)
|
||||
SendUiStateType.EditAmount -> SendAmountContent(
|
||||
amountState = uiState.editAmountState,
|
||||
isBalanceHiding = uiState.isBalanceHidden,
|
||||
clickIntents = uiState.clickIntents,
|
||||
)
|
||||
SendUiStateType.Recipient -> SendRecipientContent(
|
||||
uiState = uiState.recipientState,
|
||||
clickIntents = uiState.clickIntents,
|
||||
isBalanceHidden = uiState.isBalanceHidden,
|
||||
)
|
||||
SendUiStateType.Fee -> SendSpeedAndFeeContent(
|
||||
state = uiState.feeState,
|
||||
SendUiStateType.EditRecipient -> SendRecipientContent(
|
||||
uiState = uiState.editRecipientState,
|
||||
clickIntents = uiState.clickIntents,
|
||||
isBalanceHidden = uiState.isBalanceHidden,
|
||||
)
|
||||
SendUiStateType.EditFee -> SendSpeedAndFeeContent(
|
||||
state = uiState.editFeeState,
|
||||
clickIntents = uiState.clickIntents,
|
||||
)
|
||||
SendUiStateType.Send -> SendContent(uiState)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ internal interface SendClickIntents {
|
|||
|
||||
fun onBackClick()
|
||||
|
||||
fun onNextClick()
|
||||
fun onNextClick(isFromEdit: Boolean = false)
|
||||
|
||||
fun onPrevClick()
|
||||
|
||||
|
|
|
|||
|
|
@ -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