Updated on 2026-08-14
This commit is contained in:
parent
54cb670f22
commit
1bcc834e7e
7 changed files with 46 additions and 92 deletions
|
|
@ -453,8 +453,6 @@
|
|||
<string name="scan_card_settings_title">Приготовьте свою карту</string>
|
||||
<string name="send_additional_field_already_included">Уже содержится в введенном адресе</string>
|
||||
<string name="send_amount_label">Сумма</string>
|
||||
<string name="send_amount_substract">Вычесть из суммы отправки</string>
|
||||
<string name="send_amount_substract_footer">Сумма к получению %s</string>
|
||||
<string name="send_alert_transaction_failed_title">Транзакция не выполнена</string>
|
||||
<string name="send_alert_transaction_failed_text">Причина: %1$s\nКод: %2$s</string>
|
||||
<string name="send_alert_fee_coverage_title">Недостаточно средств для покрытия комиссии сети. Вычесть комиссию из отправляемой сумму?</string>
|
||||
|
|
|
|||
|
|
@ -455,8 +455,6 @@
|
|||
<string name="scan_card_settings_title">Get your card ready!</string>
|
||||
<string name="send_additional_field_already_included">Already included in the entered address</string>
|
||||
<string name="send_amount_label">Amount</string>
|
||||
<string name="send_amount_substract">Subtract from send amount</string>
|
||||
<string name="send_amount_substract_footer">The recipient will receive %s</string>
|
||||
<string name="send_alert_transaction_failed_title">The transaction is not completed</string>
|
||||
<string name="send_alert_transaction_failed_text">Reason: %1$s\nCode: %2$s</string>
|
||||
<string name="send_alert_fee_coverage_title">Not enough funds to cover the network commission. Subtract the commission from the amount sent?</string>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ class JobHolder {
|
|||
this.job?.cancel()
|
||||
this.job = job
|
||||
}
|
||||
|
||||
/** Cancel current [job] */
|
||||
fun cancel() {
|
||||
job?.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
fun Job.saveIn(jobHolder: JobHolder) = jobHolder.update(job = this)
|
||||
|
|
@ -14,23 +14,15 @@ internal class StateRouter(
|
|||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val isEditingDisabled: Boolean,
|
||||
) {
|
||||
private var mutableCurrentState: MutableStateFlow<SendUiCurrentScreen> = MutableStateFlow(
|
||||
if (isEditingDisabled) {
|
||||
SendUiCurrentScreen(
|
||||
type = SendUiStateType.None,
|
||||
isFromConfirmation = false,
|
||||
)
|
||||
} else {
|
||||
SendUiCurrentScreen(
|
||||
type = SendUiStateType.Recipient,
|
||||
isFromConfirmation = false,
|
||||
)
|
||||
},
|
||||
)
|
||||
private var mutableCurrentState: MutableStateFlow<SendUiCurrentScreen> = MutableStateFlow(getInitState())
|
||||
|
||||
val currentState: StateFlow<SendUiCurrentScreen>
|
||||
get() = mutableCurrentState
|
||||
|
||||
fun clear() {
|
||||
mutableCurrentState.update { getInitState() }
|
||||
}
|
||||
|
||||
fun popBackStack() {
|
||||
fragmentManager.get()?.popBackStack()
|
||||
}
|
||||
|
|
@ -128,4 +120,16 @@ internal class StateRouter(
|
|||
private fun continueToSend(show: () -> Unit) {
|
||||
if (currentState.value.isFromConfirmation) showSend() else show()
|
||||
}
|
||||
|
||||
private fun getInitState() = if (isEditingDisabled) {
|
||||
SendUiCurrentScreen(
|
||||
type = SendUiStateType.None,
|
||||
isFromConfirmation = false,
|
||||
)
|
||||
} else {
|
||||
SendUiCurrentScreen(
|
||||
type = SendUiStateType.Recipient,
|
||||
isFromConfirmation = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -34,9 +34,10 @@ 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(
|
||||
amountState = amountState.copy(
|
||||
isPrimaryButtonEnabled = !isExceedBalance,
|
||||
isPrimaryButtonEnabled = !isExceedBalance && !isZero,
|
||||
amountTextField = amountTextField.copy(
|
||||
value = cryptoValue,
|
||||
fiatValue = fiatValue,
|
||||
|
|
@ -93,9 +94,9 @@ internal class SendAmountFieldChangeConverter(
|
|||
val fiatDecimal = parseToBigDecimal(amountTextField.fiatAmount.decimals)
|
||||
val cryptoDecimal = parseToBigDecimal(amountTextField.cryptoAmount.decimals)
|
||||
return if (amountTextField.isFiatValue) {
|
||||
fiatDecimal > currencyFiatAmount || fiatDecimal.isZero()
|
||||
fiatDecimal > currencyFiatAmount
|
||||
} else {
|
||||
cryptoDecimal > currencyCryptoAmount || cryptoDecimal.isZero()
|
||||
cryptoDecimal > currencyCryptoAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
package com.tangem.features.send.impl.presentation.ui.fee
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.tangem.core.ui.components.TangemSwitch
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.ui.common.FooterContainer
|
||||
|
||||
@Composable
|
||||
internal fun SendSpeedSubtract(
|
||||
receivingAmount: String,
|
||||
isSubtract: Boolean,
|
||||
onSelectClick: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val footerText = if (isSubtract) {
|
||||
stringResource(R.string.send_amount_substract_footer, receivingAmount)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
FooterContainer(
|
||||
footer = footerText,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing16,
|
||||
horizontal = TangemTheme.dimens.spacing20,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.send_amount_substract),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier
|
||||
.padding(end = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
TangemSwitch(
|
||||
checked = isSubtract,
|
||||
onCheckedChange = onSelectClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -171,6 +171,7 @@ internal class SendViewModel @Inject constructor(
|
|||
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
|
||||
private var balanceJobHolder = JobHolder()
|
||||
private var balanceHidingJobHolder = JobHolder()
|
||||
private var recipientsJobHolder = JobHolder()
|
||||
private var feeJobHolder = JobHolder()
|
||||
private var addressValidationJobHolder = JobHolder()
|
||||
|
|
@ -180,25 +181,35 @@ internal class SendViewModel @Inject constructor(
|
|||
|
||||
private var sendIdleTimer = 0L
|
||||
|
||||
init {
|
||||
subscribeOnCurrencyStatusUpdates()
|
||||
subscribeOnBalanceHidden()
|
||||
}
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
subscribeOnCurrencyStatusUpdates(owner)
|
||||
onStateActive()
|
||||
subscribeOnBalanceHidden(owner)
|
||||
analyticsEventHandler.send(SendAnalyticEvents.SendOpened)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
balanceHidingJobHolder.cancel()
|
||||
balanceJobHolder.cancel()
|
||||
stateRouter.clear()
|
||||
}
|
||||
|
||||
fun setRouter(router: InnerSendRouter, stateRouter: StateRouter) {
|
||||
innerRouter = router
|
||||
this.stateRouter = stateRouter
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrencyStatusUpdates(owner: LifecycleOwner) {
|
||||
private fun subscribeOnCurrencyStatusUpdates() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
getUserWalletUseCase(userWalletId).fold(
|
||||
ifRight = { wallet ->
|
||||
userWallet = wallet
|
||||
checkIfSubtractAvailable()
|
||||
getCurrenciesStatusUpdates(owner, wallet)
|
||||
getCurrenciesStatusUpdates(wallet)
|
||||
},
|
||||
ifLeft = {
|
||||
uiState = eventStateFactory.getGenericErrorState(
|
||||
|
|
@ -210,23 +221,22 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnBalanceHidden(owner: LifecycleOwner) {
|
||||
private fun subscribeOnBalanceHidden() {
|
||||
getBalanceHidingSettingsUseCase()
|
||||
.flowWithLifecycle(owner.lifecycle)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
uiState = stateFactory.getOnHideBalanceState(isBalanceHidden = it.isBalanceHidden)
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
.saveIn(balanceHidingJobHolder)
|
||||
}
|
||||
|
||||
private fun getCurrenciesStatusUpdates(owner: LifecycleOwner, wallet: UserWallet) {
|
||||
private fun getCurrenciesStatusUpdates(wallet: UserWallet) {
|
||||
val isSingleWallet = wallet.scanResponse.walletData?.token != null && !wallet.isMultiCurrency
|
||||
|
||||
if (cryptoCurrency is CryptoCurrency.Coin) {
|
||||
getCurrencyStatusUpdates(isSingleWallet = isSingleWallet)
|
||||
.flowWithLifecycle(owner.lifecycle)
|
||||
.onEach { currencyStatus ->
|
||||
currencyStatus.onRight {
|
||||
onDataLoaded(
|
||||
|
|
@ -249,7 +259,7 @@ internal class SendViewModel @Inject constructor(
|
|||
coinCurrencyStatus = coinStatus.getOrElse { error("Coin status is unreachable") },
|
||||
)
|
||||
}
|
||||
}.flowWithLifecycle(owner.lifecycle)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
.saveIn(balanceJobHolder)
|
||||
|
|
@ -703,11 +713,10 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onCheckFeeUpdate() {
|
||||
val isSending = uiState.sendState.isSending
|
||||
val isSuccess = uiState.sendState.isSuccess
|
||||
val noErrorNotifications = uiState.sendState.notifications.none { it is SendNotification.Error }
|
||||
|
||||
if (!isSending && !isSuccess && noErrorNotifications) {
|
||||
if (!isSuccess && noErrorNotifications) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val feeUpdatedState = callFeeUseCase()?.fold(
|
||||
ifRight = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue