Updated on 2026-08-14
This commit is contained in:
parent
84cf236d5f
commit
c71641b451
77 changed files with 152 additions and 3065 deletions
|
|
@ -142,9 +142,7 @@ abstract class BaseTestCase : TestCase(
|
|||
with(featureTogglesManager as MutableFeatureTogglesManager) {
|
||||
changeToggle("WALLET_CONNECT_REDESIGN_ENABLED", true)
|
||||
changeToggle("WALLET_BALANCE_FETCHER_ENABLED", true)
|
||||
changeToggle("SEND_VIA_SWAP_ENABLED", true)
|
||||
changeToggle("SWAP_REDESIGN_ENABLED", true)
|
||||
changeToggle("SEND_REDESIGN_ENABLED", true)
|
||||
changeToggle("NEW_ONRAMP_MAIN_ENABLED", true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ class AmountStateConverterV2(
|
|||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val maxEnterAmount: EnterAmountBoundary,
|
||||
private val iconStateConverter: CryptoCurrencyToIconStateConverter,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
private val isBalanceHidden: Boolean,
|
||||
) : Converter<AmountParameters, AmountState> {
|
||||
|
||||
|
|
@ -122,23 +121,16 @@ class AmountStateConverterV2(
|
|||
val noFeeRate = cryptoCurrencyStatus.value.fiatRate.isNullOrZero()
|
||||
|
||||
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Loading) {
|
||||
return AmountState.Empty(
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
)
|
||||
return AmountState.Empty(isRedesignEnabled = true)
|
||||
}
|
||||
|
||||
return AmountState.Data(
|
||||
title = value.title,
|
||||
availableBalance = if (isRedesignEnabled) {
|
||||
combinedReference(
|
||||
stringReference(crypto),
|
||||
stringReference(" $DOT "),
|
||||
stringReference(fiat),
|
||||
).orMaskWithStars(isBalanceHidden)
|
||||
} else {
|
||||
resourceReference(R.string.common_crypto_fiat_format, wrappedList(crypto, fiat))
|
||||
.orMaskWithStars(isBalanceHidden)
|
||||
},
|
||||
availableBalance = combinedReference(
|
||||
stringReference(crypto),
|
||||
stringReference(" $DOT "),
|
||||
stringReference(fiat),
|
||||
).orMaskWithStars(isBalanceHidden),
|
||||
availableBalanceCrypto = stringReference(crypto).orMaskWithStars(isBalanceHidden),
|
||||
availableBalanceFiat = if (isBalanceHidden) {
|
||||
TextReference.EMPTY
|
||||
|
|
@ -171,7 +163,7 @@ class AmountStateConverterV2(
|
|||
),
|
||||
isSegmentedButtonsEnabled = !noFeeRate,
|
||||
selectedButton = 0,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
isRedesignEnabled = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.common.ui.amountScreen.converters.field
|
||||
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.core.ui.extensions.*
|
||||
|
|
@ -18,13 +17,12 @@ import com.tangem.utils.transformer.Transformer
|
|||
* @property cryptoCurrencyStatus current cryptocurrency status
|
||||
* @property maxEnterAmount new maximum enter amount boundary
|
||||
* @property appCurrency current app currency
|
||||
* @property isRedesignEnabled whether redesign mode is enabled
|
||||
* @property isBalanceHidden whether balance is hidden
|
||||
*/
|
||||
class AmountBoundaryUpdateTransformer(
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val maxEnterAmount: EnterAmountBoundary,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
private val isBalanceHidden: Boolean,
|
||||
) : Transformer<AmountState> {
|
||||
|
||||
|
|
@ -34,15 +32,11 @@ class AmountBoundaryUpdateTransformer(
|
|||
val fiat = maxEnterAmount.fiatAmount.format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
val crypto = maxEnterAmount.amount.format { crypto(cryptoCurrencyStatus.currency) }
|
||||
|
||||
val availableBalance = if (isRedesignEnabled) {
|
||||
combinedReference(
|
||||
stringReference(crypto),
|
||||
stringReference(" $DOT "),
|
||||
stringReference(fiat),
|
||||
)
|
||||
} else {
|
||||
resourceReference(R.string.common_crypto_fiat_format, wrappedList(crypto, fiat))
|
||||
}
|
||||
val availableBalance = combinedReference(
|
||||
stringReference(crypto),
|
||||
stringReference(" $DOT "),
|
||||
stringReference(fiat),
|
||||
)
|
||||
|
||||
return prevState.copy(
|
||||
availableBalance = availableBalance.orMaskWithStars(isBalanceHidden),
|
||||
|
|
|
|||
|
|
@ -23,26 +23,14 @@
|
|||
"name": "USEDESK_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "SEND_VIA_SWAP_ENABLED",
|
||||
"version": "5.28.0"
|
||||
},
|
||||
{
|
||||
"name": "SWAP_REDESIGN_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "SEND_REDESIGN_ENABLED",
|
||||
"version": "5.28.0"
|
||||
},
|
||||
{
|
||||
"name": "HOT_WALLET_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "NFT_SEND_REDESIGN_ENABLED",
|
||||
"version": "5.28.0"
|
||||
},
|
||||
{
|
||||
"name": "TANGEM_PAY_ENABLED",
|
||||
"version": "undefined"
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ fun InputRowRecipient(
|
|||
onValueChange: (String) -> Unit,
|
||||
onPasteClick: (String) -> Unit,
|
||||
onQrCodeClick: () -> Unit,
|
||||
isRedesignEnabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
singleLine: Boolean = false,
|
||||
error: TextReference? = null,
|
||||
|
|
@ -132,25 +131,15 @@ fun InputRowRecipient(
|
|||
modifier = Modifier
|
||||
.align(CenterEnd),
|
||||
) {
|
||||
if (isRedesignEnabled) {
|
||||
QrButton(
|
||||
visible = value.isBlank(),
|
||||
onQrCodeClick = onQrCodeClick,
|
||||
)
|
||||
}
|
||||
QrButton(
|
||||
visible = value.isBlank(),
|
||||
onQrCodeClick = onQrCodeClick,
|
||||
)
|
||||
PasteButton(
|
||||
isPasteButtonVisible = value.isBlank(),
|
||||
onClick = onPasteClick,
|
||||
backgroundColorEnabled = if (isRedesignEnabled) {
|
||||
TangemTheme.colors.button.secondary
|
||||
} else {
|
||||
TangemTheme.colors.button.primary
|
||||
},
|
||||
textColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors.text.primary1
|
||||
} else {
|
||||
TangemTheme.colors.text.primary2
|
||||
},
|
||||
backgroundColorEnabled = TangemTheme.colors.button.secondary,
|
||||
textColor = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
}
|
||||
|
|
@ -266,7 +255,6 @@ private fun InputRowRecipientPreview(
|
|||
onPasteClick = {},
|
||||
onQrCodeClick = {},
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
isRedesignEnabled = false,
|
||||
resolvedAddress = value.resolvedAddress,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.features.markets.portfolio.impl.loader.PortfolioData
|
||||
import com.tangem.features.markets.portfolio.impl.ui.state.TokenActionsBSContentUM
|
||||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
import com.tangem.utils.Provider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -42,7 +41,6 @@ internal class TokenActionsHandler @AssistedInject constructor(
|
|||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val messageSender: UiMessageSender,
|
||||
private val shareManager: ShareManager,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
) {
|
||||
|
||||
private val disabledActionsInDemoMode = buildSet {
|
||||
|
|
@ -159,17 +157,10 @@ internal class TokenActionsHandler @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun onSendClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) {
|
||||
val route = if (sendFeatureToggles.isSendWithSwapEnabled) {
|
||||
AppRoute.SendEntryPoint(
|
||||
userWalletId = cryptoCurrencyData.userWallet.walletId,
|
||||
currency = cryptoCurrencyData.status.currency,
|
||||
)
|
||||
} else {
|
||||
AppRoute.Send(
|
||||
userWalletId = cryptoCurrencyData.userWallet.walletId,
|
||||
currency = cryptoCurrencyData.status.currency,
|
||||
)
|
||||
}
|
||||
val route = AppRoute.SendEntryPoint(
|
||||
userWalletId = cryptoCurrencyData.userWallet.walletId,
|
||||
currency = cryptoCurrencyData.status.currency,
|
||||
)
|
||||
router.push(route)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
package com.tangem.features.send.v2.api
|
||||
|
||||
interface SendFeatureToggles {
|
||||
|
||||
val isSendRedesignEnabled: Boolean
|
||||
val isNFTSendRedesignEnabled: Boolean
|
||||
val isSendWithSwapEnabled: Boolean
|
||||
}
|
||||
interface SendFeatureToggles
|
||||
|
|
@ -17,7 +17,6 @@ sealed class DestinationUM {
|
|||
val networkName: String,
|
||||
val isValidating: Boolean = false,
|
||||
val isInitialized: Boolean = false,
|
||||
val isRedesignEnabled: Boolean = false,
|
||||
val isRecentHidden: Boolean,
|
||||
) : DestinationUM()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,5 @@
|
|||
package com.tangem.features.send.v2
|
||||
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
|
||||
internal class DefaultSendFeatureToggles(
|
||||
private val featureToggles: FeatureTogglesManager,
|
||||
) : SendFeatureToggles {
|
||||
override val isSendRedesignEnabled: Boolean
|
||||
get() = featureToggles.isFeatureEnabled("SEND_REDESIGN_ENABLED")
|
||||
override val isNFTSendRedesignEnabled: Boolean
|
||||
get() = featureToggles.isFeatureEnabled("NFT_SEND_REDESIGN_ENABLED")
|
||||
override val isSendWithSwapEnabled: Boolean
|
||||
get() = featureToggles.isFeatureEnabled("SEND_VIA_SWAP_ENABLED")
|
||||
}
|
||||
internal class DefaultSendFeatureToggles : SendFeatureToggles
|
||||
|
|
@ -32,9 +32,4 @@ internal sealed class CommonSendRoute : Route {
|
|||
data class Amount(
|
||||
override val isEditMode: Boolean,
|
||||
) : CommonSendRoute()
|
||||
|
||||
@Serializable
|
||||
data object Fee : CommonSendRoute() {
|
||||
override val isEditMode: Boolean = true
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import com.tangem.core.decompose.di.ModelComponent
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.send.v2.subcomponents.amount.model.SendAmountModel
|
||||
import com.tangem.features.send.v2.subcomponents.destination.model.SendDestinationModel
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeModel
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationsModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -26,11 +25,6 @@ internal interface CommonSendModelModule {
|
|||
@ClassKey(SendDestinationModel::class)
|
||||
fun provideSendDestinationModel(model: SendDestinationModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(SendFeeModel::class)
|
||||
fun provideSendFeeModel(model: SendFeeModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(NotificationsModel::class)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.features.send.v2.di
|
||||
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.features.send.v2.DefaultSendFeatureToggles
|
||||
import com.tangem.features.send.v2.api.*
|
||||
import com.tangem.features.send.v2.entrypoint.DefaultSendEntryPointComponent
|
||||
|
|
@ -19,8 +18,8 @@ import javax.inject.Singleton
|
|||
internal object SendFeatureModule {
|
||||
|
||||
@Provides
|
||||
fun provideSendFeatureToggles(featureTogglesManager: FeatureTogglesManager): SendFeatureToggles {
|
||||
return DefaultSendFeatureToggles(featureTogglesManager)
|
||||
fun provideSendFeatureToggles(): SendFeatureToggles {
|
||||
return DefaultSendFeatureToggles()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,8 +38,6 @@ import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent
|
|||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -118,14 +116,6 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
)
|
||||
activeComponent.updateState(model.uiState.value.destinationUM)
|
||||
}
|
||||
is SendFeeComponent -> {
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.FeeScreenOpened(
|
||||
categoryName = model.analyticCategoryName,
|
||||
source = model.analyticsSendSource,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
model.currentRoute.emit(stack.active.configuration)
|
||||
}
|
||||
|
|
@ -153,7 +143,6 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
CommonSendRoute.Empty -> getStubComponent()
|
||||
is CommonSendRoute.Destination -> getDestinationComponent(factoryContext)
|
||||
is CommonSendRoute.Amount -> getAmountComponent(factoryContext)
|
||||
is CommonSendRoute.Fee -> getFeeComponent(factoryContext)
|
||||
is CommonSendRoute.Confirm -> getConfirmComponent(factoryContext)
|
||||
is CommonSendRoute.ConfirmSuccess -> getConfirmSuccessComponent(factoryContext)
|
||||
}
|
||||
|
|
@ -188,44 +177,11 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
cryptoCurrencyStatusFlow = model.cryptoCurrencyStatusFlow,
|
||||
callback = model,
|
||||
predefinedValues = model.predefinedValues,
|
||||
isRedesignEnabled = model.uiState.value.isRedesignEnabled,
|
||||
analyticsSendSource = model.analyticsSendSource,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("ComplexCondition")
|
||||
private fun getFeeComponent(factoryContext: AppComponentContext): ComposableContentComponent {
|
||||
val state = model.uiState.value
|
||||
val sendAmount = (state.amountUM as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
|
||||
val destinationAddress = (state.destinationUM as? DestinationUM.Content)?.addressTextField?.actualAddress
|
||||
val cryptoCurrencyStatus = model.cryptoCurrencyStatusFlow.value
|
||||
val feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatusFlow.value
|
||||
|
||||
return if (sendAmount != null && destinationAddress != null) {
|
||||
SendFeeComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = SendFeeComponentParams.FeeParams(
|
||||
state = model.uiState.value.feeUM,
|
||||
currentRoute = model.currentRoute.filterIsInstance<CommonSendRoute.Fee>(),
|
||||
analyticsCategoryName = model.analyticCategoryName,
|
||||
userWallet = model.userWallet,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
appCurrency = model.appCurrency,
|
||||
onLoadFee = model::loadFee,
|
||||
sendAmount = sendAmount,
|
||||
destinationAddress = destinationAddress,
|
||||
callback = model,
|
||||
analyticsSendSource = model.analyticsSendSource,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
model.showAlertError()
|
||||
getStubComponent()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConfirmComponent(factoryContext: AppComponentContext): ComposableContentComponent {
|
||||
val cryptoCurrencyStatus = model.cryptoCurrencyStatusFlow.value
|
||||
val feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatusFlow.value
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.features.send.v2.api.entity.FeeNonce
|
||||
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationTextFieldUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
|
|
@ -19,15 +19,14 @@ internal class SendAnalyticHelper @Inject constructor(
|
|||
|
||||
fun sendSuccessAnalytics(cryptoCurrency: CryptoCurrency, sendUM: SendUM) {
|
||||
val destinationUM = sendUM.destinationUM as? DestinationUM.Content
|
||||
val feeUM = sendUM.feeUM as? FeeUM.Content
|
||||
val feeSelectorUM = feeUM?.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||
val feeType = feeSelectorUM.selectedType.toAnalyticType(feeSelectorUM)
|
||||
val feeSelectorUM = sendUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||
val feeType = feeSelectorUM.toAnalyticType()
|
||||
analyticsEventHandler.send(
|
||||
SendAnalyticEvents.TransactionScreenOpened(
|
||||
token = cryptoCurrency.symbol,
|
||||
feeType = feeType,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
nonceNotEmpty = feeSelectorUM.nonce != null,
|
||||
nonceNotEmpty = feeSelectorUM.feeNonce is FeeNonce.Nonce,
|
||||
),
|
||||
)
|
||||
analyticsEventHandler.send(
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ import com.tangem.features.send.v2.send.ui.state.SendUM
|
|||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.DefaultSendNotificationsComponent
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
|
@ -38,7 +36,7 @@ import kotlinx.coroutines.flow.*
|
|||
internal class SendConfirmComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: Params,
|
||||
private val feeSelectorComponentFactory: FeeSelectorBlockComponent.Factory,
|
||||
feeSelectorComponentFactory: FeeSelectorBlockComponent.Factory,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: SendConfirmModel = getOrCreateModel(params = params)
|
||||
|
|
@ -70,7 +68,6 @@ internal class SendConfirmComponent(
|
|||
appCurrency = params.appCurrency,
|
||||
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
|
||||
predefinedValues = params.predefinedValues,
|
||||
isRedesignEnabled = model.uiState.value.isRedesignEnabled,
|
||||
userWalletId = params.userWallet.walletId,
|
||||
cryptoCurrency = params.cryptoCurrencyStatus.currency,
|
||||
cryptoCurrencyStatusFlow = params.cryptoCurrencyStatusFlow,
|
||||
|
|
@ -81,25 +78,6 @@ internal class SendConfirmComponent(
|
|||
onClick = model::showEditAmount,
|
||||
)
|
||||
|
||||
private val feeBlockComponent = SendFeeBlockComponent(
|
||||
appComponentContext = child("sendConfirmFeeBlock"),
|
||||
params = SendFeeComponentParams.FeeBlockParams(
|
||||
state = model.uiState.value.feeUM,
|
||||
analyticsCategoryName = params.analyticsCategoryName,
|
||||
userWallet = params.userWallet,
|
||||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
|
||||
appCurrency = params.appCurrency,
|
||||
sendAmount = model.confirmData.enteredAmount.orZero(),
|
||||
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
|
||||
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
|
||||
analyticsSendSource = params.analyticsSendSource,
|
||||
onLoadFee = params.onLoadFee,
|
||||
),
|
||||
onResult = model::onFeeResult,
|
||||
onClick = model::showEditFee,
|
||||
)
|
||||
|
||||
private val feeSelectorBlockComponent = feeSelectorComponentFactory.create(
|
||||
context = appComponentContext,
|
||||
params = FeeSelectorParams.FeeSelectorBlockParams(
|
||||
|
|
@ -146,7 +124,6 @@ internal class SendConfirmComponent(
|
|||
fun updateState(state: SendUM) {
|
||||
destinationBlockComponent.updateState(state.destinationUM)
|
||||
amountBlockComponent.updateState(state.amountUM)
|
||||
feeBlockComponent.updateState(state.feeUM)
|
||||
feeSelectorBlockComponent.updateState(state.feeSelectorUM)
|
||||
model.updateState(state)
|
||||
}
|
||||
|
|
@ -160,7 +137,6 @@ internal class SendConfirmComponent(
|
|||
sendUM = state,
|
||||
destinationBlockComponent = destinationBlockComponent,
|
||||
amountBlockComponent = amountBlockComponent,
|
||||
feeBlockComponent = feeBlockComponent,
|
||||
feeSelectorBlockComponent = feeSelectorBlockComponent,
|
||||
notificationsComponent = notificationsComponent,
|
||||
notificationsUM = notificationState,
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ internal interface SendConfirmClickIntents {
|
|||
|
||||
fun showEditAmount()
|
||||
|
||||
fun showEditFee()
|
||||
|
||||
fun onSendClick()
|
||||
|
||||
fun onExploreClick()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import com.tangem.core.navigation.share.ShareManager
|
|||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
|
||||
|
|
@ -47,6 +46,7 @@ import com.tangem.features.send.v2.api.subcomponents.destination.entity.Destinat
|
|||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorCheckReloadListener
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorCheckReloadTrigger
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkAndCalculateSubtractedAmount
|
||||
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateListener
|
||||
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateTrigger
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
|
|
@ -59,13 +59,6 @@ import com.tangem.features.send.v2.send.confirm.SendConfirmComponent
|
|||
import com.tangem.features.send.v2.send.confirm.model.transformers.*
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadListener
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeData
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkAndCalculateSubtractedAmount
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.extensions.stripZeroPlainString
|
||||
|
|
@ -96,8 +89,6 @@ internal class SendConfirmModel @Inject constructor(
|
|||
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
|
||||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase,
|
||||
private val sendFeeCheckReloadTrigger: SendFeeCheckReloadTrigger,
|
||||
private val sendFeeCheckReloadListener: SendFeeCheckReloadListener,
|
||||
private val feeSelectorCheckReloadListener: FeeSelectorCheckReloadListener,
|
||||
private val feeSelectorCheckReloadTrigger: FeeSelectorCheckReloadTrigger,
|
||||
private val notificationsUpdateTrigger: SendNotificationsUpdateTrigger,
|
||||
|
|
@ -107,7 +98,6 @@ internal class SendConfirmModel @Inject constructor(
|
|||
private val urlOpener: UrlOpener,
|
||||
private val shareManager: ShareManager,
|
||||
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
|
||||
private val feeReloadTrigger: SendFeeReloadTrigger,
|
||||
private val sendAmountReduceTrigger: SendAmountReduceTrigger,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
|
||||
|
|
@ -133,10 +123,6 @@ internal class SendConfirmModel @Inject constructor(
|
|||
get() = uiState.value.amountUM as? AmountState.Data
|
||||
private val destinationUM
|
||||
get() = uiState.value.destinationUM as? DestinationUM.Content
|
||||
private val feeUM
|
||||
get() = uiState.value.feeUM as? FeeUM.Content
|
||||
private val feeSelectorUM
|
||||
get() = feeUM?.feeSelectorUM as? FeeSelectorUM.Content
|
||||
private val feeUMV2
|
||||
get() = uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Content
|
||||
|
||||
|
|
@ -147,16 +133,8 @@ internal class SendConfirmModel @Inject constructor(
|
|||
reduceAmountBy = amountState?.reduceAmountBy.orZero(),
|
||||
isIgnoreReduce = amountState?.isIgnoreReduce == true,
|
||||
enteredDestination = destinationUM?.addressTextField?.actualAddress,
|
||||
fee = if (uiState.value.isRedesignEnabled) {
|
||||
feeUMV2?.selectedFeeItem?.fee
|
||||
} else {
|
||||
feeSelectorUM?.selectedFee
|
||||
},
|
||||
feeError = if (uiState.value.isRedesignEnabled) {
|
||||
(uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Error)?.error
|
||||
} else {
|
||||
(feeUM?.feeSelectorUM as? FeeSelectorUM.Error)?.error
|
||||
},
|
||||
fee = feeUMV2?.selectedFeeItem?.fee,
|
||||
feeError = (uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Error)?.error,
|
||||
)
|
||||
|
||||
private var sendIdleTimer: Long = 0L
|
||||
|
|
@ -181,12 +159,6 @@ internal class SendConfirmModel @Inject constructor(
|
|||
updateConfirmNotifications()
|
||||
}
|
||||
|
||||
fun onFeeResult(feeUM: FeeUM) {
|
||||
sendIdleTimer = SystemClock.elapsedRealtime()
|
||||
_uiState.update { it.copy(feeUM = feeUM) }
|
||||
updateConfirmNotifications()
|
||||
}
|
||||
|
||||
fun onAmountResult(amountUM: AmountState) {
|
||||
_uiState.update { it.copy(amountUM = amountUM) }
|
||||
updateConfirmNotifications()
|
||||
|
|
@ -199,16 +171,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
|
||||
override fun onFeeReload() {
|
||||
modelScope.launch {
|
||||
if (uiState.value.isRedesignEnabled) {
|
||||
feeSelectorReloadTrigger.triggerUpdate()
|
||||
} else {
|
||||
feeReloadTrigger.triggerUpdate(
|
||||
feeData = SendFeeData(
|
||||
amount = confirmData.enteredAmount,
|
||||
destinationAddress = confirmData.enteredDestination,
|
||||
),
|
||||
)
|
||||
}
|
||||
feeSelectorReloadTrigger.triggerUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -261,30 +224,13 @@ internal class SendConfirmModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun showEditFee() {
|
||||
modelScope.launch {
|
||||
neverShowTapHelpUseCase()
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.ScreenReopened(
|
||||
categoryName = analyticsCategoryName,
|
||||
source = SendScreenSource.Fee,
|
||||
),
|
||||
)
|
||||
router.push(CommonSendRoute.Fee)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSendClick() {
|
||||
_uiState.update(SendConfirmSendingStateTransformer(isSending = true))
|
||||
if (SystemClock.elapsedRealtime() - sendIdleTimer < CHECK_FEE_UPDATE_DELAY) {
|
||||
verifyAndSendTransaction()
|
||||
} else {
|
||||
modelScope.launch {
|
||||
if (uiState.value.isRedesignEnabled) {
|
||||
feeSelectorCheckReloadTrigger.triggerCheckUpdate()
|
||||
} else {
|
||||
sendFeeCheckReloadTrigger.triggerCheckUpdate()
|
||||
}
|
||||
feeSelectorCheckReloadTrigger.triggerCheckUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -376,22 +322,12 @@ internal class SendConfirmModel @Inject constructor(
|
|||
notificationsUpdateListener.hasErrorFlow
|
||||
.onEach { hasError ->
|
||||
_uiState.update {
|
||||
if (_uiState.value.isRedesignEnabled) {
|
||||
val feeUM = it.feeSelectorUM as? FeeSelectorUMRedesigned.Content
|
||||
it.copy(
|
||||
confirmUM = (it.confirmUM as? ConfirmUM.Content)?.copy(
|
||||
isPrimaryButtonEnabled = !hasError && feeUM != null,
|
||||
) ?: it.confirmUM,
|
||||
)
|
||||
} else {
|
||||
val feeUM = it.feeUM as? FeeUM.Content
|
||||
val feeSelectorUM = feeUM?.feeSelectorUM as? FeeSelectorUM.Content
|
||||
it.copy(
|
||||
confirmUM = (it.confirmUM as? ConfirmUM.Content)?.copy(
|
||||
isPrimaryButtonEnabled = !hasError && feeSelectorUM != null,
|
||||
) ?: it.confirmUM,
|
||||
)
|
||||
}
|
||||
val feeUM = it.feeSelectorUM as? FeeSelectorUMRedesigned.Content
|
||||
it.copy(
|
||||
confirmUM = (it.confirmUM as? ConfirmUM.Content)?.copy(
|
||||
isPrimaryButtonEnabled = !hasError && feeUM != null,
|
||||
) ?: it.confirmUM,
|
||||
)
|
||||
}
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
|
|
@ -401,17 +337,8 @@ internal class SendConfirmModel @Inject constructor(
|
|||
val amountValue = amountState?.amountTextField?.cryptoAmount?.value ?: return
|
||||
val destination = destinationUM?.addressTextField?.actualAddress ?: return
|
||||
val memo = destinationUM?.memoTextField?.value
|
||||
val isRedesignEnabled = uiState.value.isRedesignEnabled
|
||||
val fee = if (isRedesignEnabled) {
|
||||
feeUMV2?.selectedFeeItem?.fee
|
||||
} else {
|
||||
feeSelectorUM?.selectedFee
|
||||
}
|
||||
val nonce = if (isRedesignEnabled) {
|
||||
(feeUMV2?.feeNonce as? FeeNonce.Nonce)?.nonce
|
||||
} else {
|
||||
feeSelectorUM?.nonce
|
||||
}
|
||||
val fee = feeUMV2?.selectedFeeItem?.fee
|
||||
val nonce = (feeUMV2?.feeNonce as? FeeNonce.Nonce)?.nonce
|
||||
val feeValue = fee?.amount?.value ?: return
|
||||
|
||||
val receivingAmount = checkAndCalculateSubtractedAmount(
|
||||
|
|
@ -478,10 +405,8 @@ internal class SendConfirmModel @Inject constructor(
|
|||
addTokenToWalletIfNeeded()
|
||||
sendBalanceUpdater.scheduleUpdates()
|
||||
sendAnalyticHelper.sendSuccessAnalytics(cryptoCurrency, uiState.value)
|
||||
if (uiState.value.isRedesignEnabled) {
|
||||
params.callback.onResult(uiState.value)
|
||||
params.onSendTransaction()
|
||||
}
|
||||
params.callback.onResult(uiState.value)
|
||||
params.onSendTransaction()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -515,15 +440,6 @@ internal class SendConfirmModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun subscribeOnCheckFeeResultUpdates() {
|
||||
sendFeeCheckReloadListener.checkReloadResultFlow.onEach { isFeeResultSuccess ->
|
||||
sendIdleTimer = SystemClock.elapsedRealtime()
|
||||
if (isFeeResultSuccess) {
|
||||
_uiState.update(SendConfirmSendingStateTransformer(isSending = true))
|
||||
verifyAndSendTransaction()
|
||||
} else {
|
||||
_uiState.update(SendConfirmSendingStateTransformer(isSending = false))
|
||||
}
|
||||
}.launchIn(modelScope)
|
||||
feeSelectorCheckReloadListener.checkReloadResultFlow.onEach { isFeeResultSuccess ->
|
||||
sendIdleTimer = SystemClock.elapsedRealtime()
|
||||
if (isFeeResultSuccess) {
|
||||
|
|
@ -560,25 +476,14 @@ internal class SendConfirmModel @Inject constructor(
|
|||
)
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
confirmUM = if (uiState.value.isRedesignEnabled) {
|
||||
SendConfirmationNotificationsTransformerV2(
|
||||
feeSelectorUM = uiState.value.feeSelectorUM,
|
||||
amountUM = uiState.value.amountUM,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
appCurrency = appCurrency,
|
||||
analyticsCategoryName = params.analyticsCategoryName,
|
||||
).transform(uiState.value.confirmUM)
|
||||
} else {
|
||||
SendConfirmationNotificationsTransformer(
|
||||
feeUM = uiState.value.feeUM,
|
||||
amountUM = uiState.value.amountUM,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
appCurrency = appCurrency,
|
||||
analyticsCategoryName = params.analyticsCategoryName,
|
||||
).transform(uiState.value.confirmUM)
|
||||
},
|
||||
confirmUM = SendConfirmationNotificationsTransformerV2(
|
||||
feeSelectorUM = uiState.value.feeSelectorUM,
|
||||
amountUM = uiState.value.amountUM,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
appCurrency = appCurrency,
|
||||
analyticsCategoryName = params.analyticsCategoryName,
|
||||
).transform(uiState.value.confirmUM),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -593,31 +498,15 @@ internal class SendConfirmModel @Inject constructor(
|
|||
).filter {
|
||||
it.second is CommonSendRoute.Confirm
|
||||
}.onEach { (state, _) ->
|
||||
val amountUM = state.amountUM as? AmountState.Data
|
||||
val confirmUM = state.confirmUM
|
||||
params.callback.onResult(
|
||||
state.copy(
|
||||
navigationUM = NavigationUM.Content(
|
||||
title = if (state.isRedesignEnabled) {
|
||||
resourceReference(id = R.string.common_send)
|
||||
} else {
|
||||
resourceReference(
|
||||
id = R.string.send_summary_title,
|
||||
formatArgs = wrappedList(params.cryptoCurrencyStatus.currency.name),
|
||||
)
|
||||
},
|
||||
subtitle = if (uiState.value.isRedesignEnabled) {
|
||||
null
|
||||
} else {
|
||||
amountUM?.title
|
||||
},
|
||||
backIconRes = if (state.isRedesignEnabled) {
|
||||
when (confirmUM) {
|
||||
is ConfirmUM.Success -> R.drawable.ic_close_24
|
||||
else -> R.drawable.ic_back_24
|
||||
}
|
||||
} else {
|
||||
R.drawable.ic_close_24
|
||||
title = resourceReference(id = R.string.common_send),
|
||||
subtitle = null,
|
||||
backIconRes = when (confirmUM) {
|
||||
is ConfirmUM.Success -> R.drawable.ic_close_24
|
||||
else -> R.drawable.ic_back_24
|
||||
},
|
||||
backIconClick = {
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -628,11 +517,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
isValid = confirmUM.isPrimaryButtonEnabled,
|
||||
),
|
||||
)
|
||||
if (state.isRedesignEnabled) {
|
||||
router.pop()
|
||||
} else {
|
||||
appRouter.pop()
|
||||
}
|
||||
router.pop()
|
||||
},
|
||||
primaryButton = primaryButtonUM(),
|
||||
prevButton = null,
|
||||
|
|
|
|||
|
|
@ -1,121 +0,0 @@
|
|||
package com.tangem.features.send.v2.send.confirm.model.transformers
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||
import com.tangem.features.send.v2.api.utils.formatFooterFiatFee
|
||||
import com.tangem.features.send.v2.api.utils.getTronTokenFeeSendingText
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkIfFeeTooHigh
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal class SendConfirmationNotificationsTransformer(
|
||||
private val feeUM: FeeUM,
|
||||
private val amountUM: AmountState,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val cryptoCurrency: CryptoCurrency,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val analyticsCategoryName: String,
|
||||
) : Transformer<ConfirmUM> {
|
||||
override fun transform(prevState: ConfirmUM): ConfirmUM {
|
||||
val state = prevState as? ConfirmUM.Content ?: return prevState
|
||||
val feeUM = feeUM as? FeeUM.Content ?: return prevState
|
||||
return state.copy(
|
||||
sendingFooter = getSendingFooterText(),
|
||||
notifications = buildList {
|
||||
addTooHighNotification(feeUM.feeSelectorUM)
|
||||
addTooLowNotification(feeUM)
|
||||
}.toPersistentList(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addTooLowNotification(feeUM: FeeUM.Content) {
|
||||
val feeSelectorUM = feeUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||
val multipleFees = feeSelectorUM.fees as? TransactionFee.Choosable ?: return
|
||||
val minimumValue = multipleFees.minimum.amount.value ?: return
|
||||
val customAmount = feeSelectorUM.customValues.firstOrNull() ?: return
|
||||
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
|
||||
if (feeSelectorUM.selectedType == FeeType.Custom && minimumValue > customValue) {
|
||||
add(NotificationUM.Warning.FeeTooLow)
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.NoticeTransactionDelays(
|
||||
categoryName = analyticsCategoryName,
|
||||
token = cryptoCurrency.symbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addTooHighNotification(feeSelectorUM: FeeSelectorUM) {
|
||||
if (feeSelectorUM !is FeeSelectorUM.Content) return
|
||||
|
||||
val (isFeeTooHigh, diff) = checkIfFeeTooHigh(feeSelectorUM)
|
||||
if (isFeeTooHigh) {
|
||||
add(NotificationUM.Warning.TooHigh(diff))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSendingFooterText(): TextReference {
|
||||
val feeUM = feeUM as? FeeUM.Content
|
||||
val amountUM = amountUM as? AmountState.Data
|
||||
val fee = (feeUM?.feeSelectorUM as? FeeSelectorUM.Content)?.selectedFee
|
||||
|
||||
if (fee == null || amountUM == null) return TextReference.EMPTY
|
||||
|
||||
val fiatAmountValue = amountUM.amountTextField.fiatAmount.value
|
||||
val fiatFeeValue = feeUM.rate?.let { fee.amount.value?.multiply(it) }
|
||||
|
||||
val fiatSendingValue = if (feeUM.isFeeConvertibleToFiat) {
|
||||
fiatFeeValue?.let { fiatAmountValue?.plus(it) }
|
||||
} else {
|
||||
fiatAmountValue
|
||||
}
|
||||
|
||||
val fiatSending = fiatSendingValue.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
}
|
||||
val fiatFee = formatFooterFiatFee(
|
||||
amount = fee.amount.copy(value = fiatFeeValue),
|
||||
isFeeConvertibleToFiat = feeUM.isFeeConvertibleToFiat,
|
||||
isFeeApproximate = feeUM.isFeeApproximate,
|
||||
appCurrency = appCurrency,
|
||||
)
|
||||
|
||||
return if (feeUM.isTronToken && fee is Fee.Tron) {
|
||||
getTronTokenFeeSendingText(
|
||||
fee = fee,
|
||||
fiatFee = fiatFee,
|
||||
fiatSending = stringReference(fiatSending),
|
||||
)
|
||||
} else {
|
||||
resourceReference(
|
||||
id = if (feeUM.isFeeConvertibleToFiat) {
|
||||
R.string.send_summary_transaction_description
|
||||
} else {
|
||||
R.string.send_summary_transaction_description_no_fiat_fee
|
||||
},
|
||||
formatArgs = wrappedList(fiatSending, fiatFee),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.features.send.v2.send.confirm.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -9,26 +8,18 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.core.ui.utils.toTimeFormat
|
||||
import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.tapHelp
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.notifications
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.DefaultSendNotificationsComponent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -41,7 +32,6 @@ internal fun SendConfirmContent(
|
|||
sendUM: SendUM,
|
||||
destinationBlockComponent: DefaultSendDestinationBlockComponent,
|
||||
amountBlockComponent: SendAmountBlockComponent,
|
||||
feeBlockComponent: SendFeeBlockComponent,
|
||||
feeSelectorBlockComponent: FeeSelectorBlockComponent,
|
||||
notificationsComponent: DefaultSendNotificationsComponent,
|
||||
notificationsUM: ImmutableList<NotificationUM>,
|
||||
|
|
@ -57,10 +47,8 @@ internal fun SendConfirmContent(
|
|||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
blocks(
|
||||
uiState = sendUM,
|
||||
destinationBlockComponent = destinationBlockComponent,
|
||||
amountBlockComponent = amountBlockComponent,
|
||||
feeBlockComponent = feeBlockComponent,
|
||||
feeSelectorBlockComponent = feeSelectorBlockComponent,
|
||||
)
|
||||
if (confirmUM != null) {
|
||||
|
|
@ -84,49 +72,19 @@ internal fun SendConfirmContent(
|
|||
}
|
||||
|
||||
private fun LazyListScope.blocks(
|
||||
uiState: SendUM,
|
||||
destinationBlockComponent: DefaultSendDestinationBlockComponent,
|
||||
amountBlockComponent: SendAmountBlockComponent,
|
||||
feeBlockComponent: SendFeeBlockComponent,
|
||||
feeSelectorBlockComponent: FeeSelectorBlockComponent,
|
||||
) {
|
||||
item(key = BLOCKS_KEY) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
if (uiState.isRedesignEnabled) {
|
||||
amountBlockComponent.Content(modifier = Modifier)
|
||||
destinationBlockComponent.Content(modifier = Modifier)
|
||||
feeSelectorBlockComponent.Content(
|
||||
modifier = Modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
} else {
|
||||
TransactionDoneTitleAnimated(uiState)
|
||||
destinationBlockComponent.Content(modifier = Modifier)
|
||||
amountBlockComponent.Content(modifier = Modifier)
|
||||
feeBlockComponent.Content(modifier = Modifier)
|
||||
}
|
||||
amountBlockComponent.Content(modifier = Modifier)
|
||||
destinationBlockComponent.Content(modifier = Modifier)
|
||||
feeSelectorBlockComponent.Content(
|
||||
modifier = Modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun TransactionDoneTitleAnimated(uiState: SendUM) {
|
||||
AnimatedVisibility(
|
||||
visible = uiState.confirmUM is ConfirmUM.Success,
|
||||
modifier = Modifier.padding(vertical = 12.dp),
|
||||
) {
|
||||
val wrappedConfirmUM = remember(this) { uiState.confirmUM as ConfirmUM.Success }
|
||||
TransactionDoneTitle(
|
||||
title = resourceReference(R.string.sent_transaction_sent_title),
|
||||
subtitle = resourceReference(
|
||||
R.string.send_date_format,
|
||||
wrappedList(
|
||||
wrappedConfirmUM.transactionDate.toTimeFormat(DateTimeFormatters.dateFormatter),
|
||||
wrappedConfirmUM.transactionDate.toTimeFormat(),
|
||||
),
|
||||
),
|
||||
modifier = Modifier.padding(vertical = 12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +39,6 @@ import com.tangem.domain.utils.convertToSdkAmount
|
|||
import com.tangem.domain.wallets.models.GetUserWalletError
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.send.v2.api.SendComponent
|
||||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.SendScreenSource
|
||||
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
||||
|
|
@ -57,8 +56,6 @@ import com.tangem.features.send.v2.send.ui.state.SendUM
|
|||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.destination.model.transformers.SendDestinationInitialStateTransformer
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
|
|
@ -70,7 +67,6 @@ import kotlin.properties.Delegates
|
|||
|
||||
internal interface SendComponentCallback :
|
||||
SendAmountComponent.ModelCallback,
|
||||
SendFeeComponent.ModelCallback,
|
||||
SendDestinationComponent.ModelCallback,
|
||||
SendConfirmComponent.ModelCallback,
|
||||
SendConfirmSuccessComponent.ModelCallback
|
||||
|
|
@ -96,7 +92,6 @@ internal class SendModel @Inject constructor(
|
|||
private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
|
||||
private val getFeeUseCase: GetFeeUseCase,
|
||||
private val sendAmountUpdateTrigger: SendAmountUpdateTrigger,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Model(), SendComponentCallback {
|
||||
|
||||
|
|
@ -113,11 +108,7 @@ internal class SendModel @Inject constructor(
|
|||
field = MutableStateFlow(false)
|
||||
|
||||
val initialRoute = if (params.amount == null) {
|
||||
if (uiState.value.isRedesignEnabled) {
|
||||
Amount(isEditMode = false)
|
||||
} else {
|
||||
Destination(isEditMode = false)
|
||||
}
|
||||
Amount(isEditMode = false)
|
||||
} else {
|
||||
Empty
|
||||
}
|
||||
|
|
@ -167,19 +158,13 @@ internal class SendModel @Inject constructor(
|
|||
uiState.update { it.copy(amountUM = amountUM) }
|
||||
}
|
||||
|
||||
override fun onFeeResult(feeUM: FeeUM) {
|
||||
uiState.update { it.copy(feeUM = feeUM) }
|
||||
}
|
||||
|
||||
override fun onResult(sendUM: SendUM) {
|
||||
uiState.update { sendUM }
|
||||
}
|
||||
|
||||
override fun onBackClick() {
|
||||
val isRedesignEnabled = uiState.value.isRedesignEnabled
|
||||
|
||||
when (val route = currentRoute.value) {
|
||||
is Amount -> if (isRedesignEnabled && !route.isEditMode) {
|
||||
is Amount -> if (!route.isEditMode) {
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.CloseButtonClicked(
|
||||
categoryName = analyticCategoryName,
|
||||
|
|
@ -189,7 +174,7 @@ internal class SendModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
}
|
||||
is Destination -> if (isRedesignEnabled && !route.isEditMode) {
|
||||
is Destination -> if (!route.isEditMode) {
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.CloseButtonClicked(
|
||||
categoryName = analyticCategoryName,
|
||||
|
|
@ -206,21 +191,12 @@ internal class SendModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onNextClick() {
|
||||
val isRedesignEnabled = uiState.value.isRedesignEnabled
|
||||
if (currentRoute.value.isEditMode) {
|
||||
onBackClick()
|
||||
} else {
|
||||
when (currentRoute.value) {
|
||||
is Amount -> if (isRedesignEnabled) {
|
||||
router.push(Destination(isEditMode = false))
|
||||
} else {
|
||||
router.push(Confirm)
|
||||
}
|
||||
is Destination -> if (isRedesignEnabled) {
|
||||
router.push(Confirm)
|
||||
} else {
|
||||
router.push(Amount(isEditMode = false))
|
||||
}
|
||||
is Amount -> router.push(Destination(isEditMode = false))
|
||||
is Destination -> router.push(Confirm)
|
||||
Confirm -> router.push(ConfirmSuccess)
|
||||
else -> onBackClick()
|
||||
}
|
||||
|
|
@ -242,9 +218,7 @@ internal class SendModel @Inject constructor(
|
|||
it.copy(
|
||||
destinationUM = SendDestinationInitialStateTransformer(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled,
|
||||
).transform(DestinationUM.Empty()),
|
||||
feeUM = FeeUM.Empty(),
|
||||
feeSelectorUM = FeeSelectorUM.Loading,
|
||||
confirmUM = ConfirmUM.Empty,
|
||||
confirmData = null,
|
||||
|
|
@ -480,15 +454,12 @@ internal class SendModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun initialState(): SendUM = SendUM(
|
||||
amountUM = AmountState.Empty(isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled),
|
||||
amountUM = AmountState.Empty(isRedesignEnabled = true),
|
||||
destinationUM = SendDestinationInitialStateTransformer(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled,
|
||||
).transform(DestinationUM.Empty()),
|
||||
feeUM = FeeUM.Empty(),
|
||||
confirmUM = ConfirmUM.Empty,
|
||||
navigationUM = NavigationUM.Empty,
|
||||
isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled,
|
||||
confirmData = null,
|
||||
feeSelectorUM = FeeSelectorUM.Loading,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,15 +6,12 @@ import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
|||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.send.confirm.model.ConfirmData
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
|
||||
internal data class SendUM(
|
||||
val amountUM: AmountState,
|
||||
val destinationUM: DestinationUM,
|
||||
val feeUM: FeeUM,
|
||||
val feeSelectorUM: FeeSelectorUM,
|
||||
val confirmUM: ConfirmUM,
|
||||
val navigationUM: NavigationUM,
|
||||
val isRedesignEnabled: Boolean,
|
||||
val confirmData: ConfirmData?,
|
||||
)
|
||||
|
|
@ -21,7 +21,6 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.features.send.v2.api.NFTSendComponent
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.ui.SendContent
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
|
|
@ -30,14 +29,11 @@ import com.tangem.features.send.v2.sendnft.confirm.NFTSendConfirmComponent
|
|||
import com.tangem.features.send.v2.sendnft.model.NFTSendModel
|
||||
import com.tangem.features.send.v2.sendnft.success.NFTSendSuccessComponent
|
||||
import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class DefaultNFTSendComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
|
|
@ -102,14 +98,6 @@ internal class DefaultNFTSendComponent @AssistedInject constructor(
|
|||
)
|
||||
activeComponent.updateState(model.uiState.value.destinationUM)
|
||||
}
|
||||
is SendFeeComponent -> {
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.FeeScreenOpened(
|
||||
categoryName = analyticsCategoryName,
|
||||
source = analyticsSendSource,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
model.currentRouteFlow.emit(stack.active.configuration)
|
||||
}
|
||||
|
|
@ -131,7 +119,6 @@ internal class DefaultNFTSendComponent @AssistedInject constructor(
|
|||
|
||||
private fun createChild(route: CommonSendRoute, factoryContext: AppComponentContext) = when (route) {
|
||||
is CommonSendRoute.Destination -> getDestinationComponent(factoryContext)
|
||||
is CommonSendRoute.Fee -> getFeeComponent(factoryContext)
|
||||
CommonSendRoute.Confirm -> getConfirmComponent(factoryContext)
|
||||
CommonSendRoute.ConfirmSuccess -> getSuccessComponent(factoryContext)
|
||||
else -> getStubComponent()
|
||||
|
|
@ -153,32 +140,6 @@ internal class DefaultNFTSendComponent @AssistedInject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
private fun getFeeComponent(factoryContext: AppComponentContext): ComposableContentComponent {
|
||||
val state = model.uiState.value
|
||||
val destinationAddress = (state.destinationUM as? DestinationUM.Content)?.addressTextField?.actualAddress
|
||||
return if (destinationAddress != null) {
|
||||
SendFeeComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = SendFeeComponentParams.FeeParams(
|
||||
state = model.uiState.value.feeUM,
|
||||
currentRoute = model.currentRouteFlow.filterIsInstance<CommonSendRoute.Fee>(),
|
||||
analyticsCategoryName = analyticsCategoryName,
|
||||
userWallet = model.userWallet,
|
||||
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatus,
|
||||
appCurrency = model.appCurrency,
|
||||
sendAmount = BigDecimal.ZERO,
|
||||
onLoadFee = model::loadFee,
|
||||
destinationAddress = destinationAddress,
|
||||
callback = model,
|
||||
analyticsSendSource = analyticsSendSource,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
getStubComponent()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConfirmComponent(factoryContext: AppComponentContext) = nftSendConfirmComponentFactory.create(
|
||||
appComponentContext = factoryContext,
|
||||
params = NFTSendConfirmComponent.Params(
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import com.tangem.core.analytics.models.Basic
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.Companion.NFT_SEND_CATEGORY
|
||||
import com.tangem.features.send.v2.api.entity.FeeNonce
|
||||
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationTextFieldUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
|
|
@ -20,15 +20,14 @@ internal class NFTSendAnalyticHelper @Inject constructor(
|
|||
|
||||
fun nftSendSuccessAnalytics(cryptoCurrency: CryptoCurrency, nftSendUM: NFTSendUM) {
|
||||
val destinationUM = nftSendUM.destinationUM as? DestinationUM.Content
|
||||
val feeUM = nftSendUM.feeUM as? FeeUM.Content
|
||||
val feeSelectorUM = feeUM?.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||
val feeType = feeSelectorUM.selectedType.toAnalyticType(feeSelectorUM)
|
||||
val feeSelectorUM = nftSendUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||
val feeType = feeSelectorUM.toAnalyticType()
|
||||
analyticsEventHandler.send(
|
||||
NFTSendAnalyticEvents.TransactionScreenOpened(
|
||||
token = cryptoCurrency.symbol,
|
||||
feeType = feeType,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
nonceNotEmpty = feeSelectorUM.nonce != null,
|
||||
nonceNotEmpty = feeSelectorUM.feeNonce is FeeNonce.Nonce,
|
||||
),
|
||||
)
|
||||
analyticsEventHandler.send(
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ import com.tangem.features.send.v2.sendnft.confirm.model.NFTSendConfirmModel
|
|||
import com.tangem.features.send.v2.sendnft.confirm.ui.NFTSendConfirmContent
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.DefaultSendNotificationsComponent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -68,25 +66,6 @@ internal class NFTSendConfirmComponent @AssistedInject constructor(
|
|||
onClick = model::showEditDestination,
|
||||
)
|
||||
|
||||
private val feeBlockComponent = SendFeeBlockComponent(
|
||||
appComponentContext = child("NFTSendConfirmFeeBlock"),
|
||||
params = SendFeeComponentParams.FeeBlockParams(
|
||||
state = model.uiState.value.feeUM,
|
||||
analyticsCategoryName = params.analyticsCategoryName,
|
||||
userWallet = params.userWallet,
|
||||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
|
||||
appCurrency = params.appCurrency,
|
||||
sendAmount = BigDecimal.ZERO,
|
||||
onLoadFee = params.onLoadFee,
|
||||
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
|
||||
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
|
||||
analyticsSendSource = params.analyticsSendSource,
|
||||
),
|
||||
onResult = model::onFeeResult,
|
||||
onClick = model::showEditFee,
|
||||
)
|
||||
|
||||
private val feeSelectorBlockComponent = feeSelectorComponentFactory.create(
|
||||
context = child("NFTSendConfirmFeeSelectorBlock"),
|
||||
params = FeeSelectorParams.FeeSelectorBlockParams(
|
||||
|
|
@ -143,7 +122,6 @@ internal class NFTSendConfirmComponent @AssistedInject constructor(
|
|||
|
||||
fun updateState(state: NFTSendUM) {
|
||||
destinationBlockComponent.updateState(state.destinationUM)
|
||||
feeBlockComponent.updateState(state.feeUM)
|
||||
model.updateState(state)
|
||||
}
|
||||
|
||||
|
|
@ -155,7 +133,6 @@ internal class NFTSendConfirmComponent @AssistedInject constructor(
|
|||
NFTSendConfirmContent(
|
||||
nftSendUM = state,
|
||||
destinationBlockComponent = destinationBlockComponent,
|
||||
feeBlockComponent = feeBlockComponent,
|
||||
feeSelectorBlockComponent = feeSelectorBlockComponent,
|
||||
nftDetailsBlockComponent = nftDetailsBlockComponent,
|
||||
notificationsComponent = notificationsComponent,
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ internal interface NFTSendConfirmClickIntents {
|
|||
|
||||
fun showEditDestination()
|
||||
|
||||
fun showEditFee()
|
||||
|
||||
fun onSendClick()
|
||||
|
||||
fun onExploreClick()
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback
|
|||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorCheckReloadListener
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorCheckReloadTrigger
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
|
||||
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateListener
|
||||
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateTrigger
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
|
|
@ -47,15 +48,8 @@ import com.tangem.features.send.v2.sendnft.analytics.NFTSendAnalyticHelper
|
|||
import com.tangem.features.send.v2.sendnft.confirm.NFTSendConfirmComponent
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendConfirmInitialStateTransformer
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendConfirmSendingStateTransformer
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendConfirmationNotificationsTransformer
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendConfirmationNotificationsTransformerV2
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadListener
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeData
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.stripZeroPlainString
|
||||
import com.tangem.utils.transformer.update
|
||||
|
|
@ -83,8 +77,6 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val notificationsUpdateTrigger: SendNotificationsUpdateTrigger,
|
||||
private val notificationsUpdateListener: SendNotificationsUpdateListener,
|
||||
private val sendFeeCheckReloadTrigger: SendFeeCheckReloadTrigger,
|
||||
private val sendFeeCheckReloadListener: SendFeeCheckReloadListener,
|
||||
private val feeSelectorCheckReloadTrigger: FeeSelectorCheckReloadTrigger,
|
||||
private val feeSelectorCheckReloadListener: FeeSelectorCheckReloadListener,
|
||||
private val alertFactory: SendConfirmAlertFactory,
|
||||
|
|
@ -93,7 +85,7 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val nftSendAnalyticHelper: NFTSendAnalyticHelper,
|
||||
private val nftSendSuccessTrigger: NFTSendSuccessTrigger,
|
||||
private val sendFeeReloadTrigger: SendFeeReloadTrigger,
|
||||
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
|
||||
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
|
||||
) : Model(), NFTSendConfirmClickIntents, SendNotificationsComponent.ModelCallback, FeeSelectorModelCallback {
|
||||
|
||||
|
|
@ -114,25 +106,13 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
|
||||
private val destinationUM
|
||||
get() = uiState.value.destinationUM as? DestinationUM.Content
|
||||
private val feeUM
|
||||
get() = uiState.value.feeUM as? FeeUM.Content
|
||||
private val feeSelectorUM
|
||||
get() = feeUM?.feeSelectorUM as? FeeSelectorUM.Content
|
||||
|
||||
val confirmData: ConfirmData
|
||||
get() = ConfirmData(
|
||||
enteredDestination = destinationUM?.addressTextField?.actualAddress,
|
||||
enteredMemo = destinationUM?.memoTextField?.value,
|
||||
fee = if (uiState.value.isRedesignEnabled) {
|
||||
(uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Content)?.selectedFeeItem?.fee
|
||||
} else {
|
||||
feeSelectorUM?.selectedFee
|
||||
},
|
||||
feeError = if (uiState.value.isRedesignEnabled) {
|
||||
(uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Error)?.error
|
||||
} else {
|
||||
(feeUM?.feeSelectorUM as? FeeSelectorUM.Error)?.error
|
||||
},
|
||||
fee = (uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Content)?.selectedFeeItem?.fee,
|
||||
feeError = (uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Error)?.error,
|
||||
)
|
||||
|
||||
private var sendIdleTimer: Long = 0L
|
||||
|
|
@ -150,12 +130,6 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
updateConfirmNotifications()
|
||||
}
|
||||
|
||||
fun onFeeResult(feeUM: FeeUM) {
|
||||
sendIdleTimer = SystemClock.elapsedRealtime()
|
||||
_uiState.update { it.copy(feeUM = feeUM) }
|
||||
updateConfirmNotifications()
|
||||
}
|
||||
|
||||
override fun onFeeResult(feeSelectorUM: FeeSelectorUMRedesigned) {
|
||||
sendIdleTimer = SystemClock.elapsedRealtime()
|
||||
_uiState.update { it.copy(feeSelectorUM = feeSelectorUM) }
|
||||
|
|
@ -169,13 +143,7 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
|
||||
override fun onFeeReload() {
|
||||
modelScope.launch {
|
||||
sendFeeReloadTrigger.triggerUpdate(
|
||||
// For now transfer one nft asset at a time
|
||||
feeData = SendFeeData(
|
||||
amount = BigDecimal.ZERO,
|
||||
destinationAddress = confirmData.enteredDestination,
|
||||
),
|
||||
)
|
||||
feeSelectorReloadTrigger.triggerUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -192,30 +160,13 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun showEditFee() {
|
||||
modelScope.launch {
|
||||
neverShowTapHelpUseCase()
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.ScreenReopened(
|
||||
categoryName = analyticsCategoryName,
|
||||
source = SendScreenSource.Fee,
|
||||
),
|
||||
)
|
||||
router.push(CommonSendRoute.Fee)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSendClick() {
|
||||
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = true))
|
||||
if (SystemClock.elapsedRealtime() - sendIdleTimer < CHECK_FEE_UPDATE_DELAY) {
|
||||
verifyAndSendTransaction()
|
||||
} else {
|
||||
modelScope.launch {
|
||||
if (uiState.value.isRedesignEnabled) {
|
||||
feeSelectorCheckReloadTrigger.triggerCheckUpdate()
|
||||
} else {
|
||||
sendFeeCheckReloadTrigger.triggerCheckUpdate()
|
||||
}
|
||||
feeSelectorCheckReloadTrigger.triggerCheckUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -257,11 +208,7 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
|
||||
private fun initialState() {
|
||||
val confirmUM = uiState.value.confirmUM
|
||||
val isEmptyFee = if (uiState.value.isRedesignEnabled) {
|
||||
uiState.value.feeSelectorUM !is FeeSelectorUMRedesigned.Content
|
||||
} else {
|
||||
uiState.value.feeUM is FeeUM.Empty
|
||||
}
|
||||
val isEmptyFee = uiState.value.feeSelectorUM !is FeeSelectorUMRedesigned.Content
|
||||
|
||||
modelScope.launch {
|
||||
val isShowTapHelp = isSendTapHelpEnabledUseCase.invokeSync().getOrElse { false }
|
||||
|
|
@ -293,13 +240,7 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
notificationsUpdateListener.hasErrorFlow
|
||||
.onEach { hasError ->
|
||||
_uiState.update {
|
||||
val isFeeNotNull = if (uiState.value.isRedesignEnabled) {
|
||||
it.feeSelectorUM is FeeSelectorUMRedesigned.Content
|
||||
} else {
|
||||
val feeUM = it.feeUM as? FeeUM.Content
|
||||
val feeSelectorUM = feeUM?.feeSelectorUM as? FeeSelectorUM.Content
|
||||
feeSelectorUM != null
|
||||
}
|
||||
val isFeeNotNull = it.feeSelectorUM is FeeSelectorUMRedesigned.Content
|
||||
|
||||
it.copy(
|
||||
confirmUM = (it.confirmUM as? ConfirmUM.Content)?.copy(
|
||||
|
|
@ -372,24 +313,13 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
updateTransactionStatus(txData)
|
||||
sendBalanceUpdater.scheduleUpdates()
|
||||
nftSendAnalyticHelper.nftSendSuccessAnalytics(cryptoCurrency, uiState.value)
|
||||
if (uiState.value.isRedesignEnabled) {
|
||||
params.callback.onResult(uiState.value)
|
||||
params.onSendTransaction()
|
||||
}
|
||||
params.callback.onResult(uiState.value)
|
||||
params.onSendTransaction()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun subscribeOnCheckFeeResultUpdates() {
|
||||
sendFeeCheckReloadListener.checkReloadResultFlow.onEach { isFeeResultSuccess ->
|
||||
sendIdleTimer = SystemClock.elapsedRealtime()
|
||||
if (isFeeResultSuccess) {
|
||||
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = true))
|
||||
verifyAndSendTransaction()
|
||||
} else {
|
||||
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
|
||||
}
|
||||
}.launchIn(modelScope)
|
||||
feeSelectorCheckReloadListener.checkReloadResultFlow.onEach { isFeeResultSuccess ->
|
||||
sendIdleTimer = SystemClock.elapsedRealtime()
|
||||
if (isFeeResultSuccess) {
|
||||
|
|
@ -425,23 +355,13 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
}
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
confirmUM = if (uiState.value.isRedesignEnabled) {
|
||||
NFTSendConfirmationNotificationsTransformerV2(
|
||||
feeSelectorUM = uiState.value.feeSelectorUM,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
appCurrency = params.appCurrency,
|
||||
analyticsCategoryName = analyticsCategoryName,
|
||||
)
|
||||
} else {
|
||||
NFTSendConfirmationNotificationsTransformer(
|
||||
feeUM = uiState.value.feeUM,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
analyticsCategoryName = analyticsCategoryName,
|
||||
appCurrency = params.appCurrency,
|
||||
)
|
||||
}.transform(uiState.value.confirmUM),
|
||||
confirmUM = NFTSendConfirmationNotificationsTransformerV2(
|
||||
feeSelectorUM = uiState.value.feeSelectorUM,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
appCurrency = params.appCurrency,
|
||||
analyticsCategoryName = analyticsCategoryName,
|
||||
).transform(uiState.value.confirmUM),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -453,23 +373,14 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
transform = { state, route -> state to route },
|
||||
).onEach { (state, _) ->
|
||||
val confirmUM = state.confirmUM
|
||||
val confirmUMContent = confirmUM as? ConfirmUM.Content
|
||||
params.callback.onResult(
|
||||
state.copy(
|
||||
navigationUM = NavigationUM.Content(
|
||||
title = resourceReference(R.string.nft_send),
|
||||
subtitle = if (uiState.value.isRedesignEnabled) {
|
||||
null
|
||||
} else {
|
||||
confirmUMContent?.walletName
|
||||
},
|
||||
backIconRes = if (state.isRedesignEnabled) {
|
||||
when (confirmUM) {
|
||||
is ConfirmUM.Success -> R.drawable.ic_close_24
|
||||
else -> R.drawable.ic_back_24
|
||||
}
|
||||
} else {
|
||||
R.drawable.ic_close_24
|
||||
subtitle = null,
|
||||
backIconRes = when (confirmUM) {
|
||||
is ConfirmUM.Success -> R.drawable.ic_close_24
|
||||
else -> R.drawable.ic_back_24
|
||||
},
|
||||
backIconClick = {
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -480,11 +391,7 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
isValid = confirmUM.isPrimaryButtonEnabled,
|
||||
),
|
||||
)
|
||||
if (state.isRedesignEnabled) {
|
||||
router.pop()
|
||||
} else {
|
||||
appRouter.pop()
|
||||
}
|
||||
router.pop()
|
||||
},
|
||||
primaryButton = primaryButtonUM(),
|
||||
prevButton = null,
|
||||
|
|
|
|||
|
|
@ -1,103 +0,0 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm.model.transformers
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||
import com.tangem.features.send.v2.api.utils.formatFooterFiatFee
|
||||
import com.tangem.features.send.v2.api.utils.getTronTokenFeeSendingText
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkIfFeeTooHigh
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal class NFTSendConfirmationNotificationsTransformer(
|
||||
private val feeUM: FeeUM,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val cryptoCurrency: CryptoCurrency,
|
||||
private val analyticsCategoryName: String,
|
||||
private val appCurrency: AppCurrency,
|
||||
) : Transformer<ConfirmUM> {
|
||||
override fun transform(prevState: ConfirmUM): ConfirmUM {
|
||||
val state = prevState as? ConfirmUM.Content ?: return prevState
|
||||
val feeUM = feeUM as? FeeUM.Content ?: return prevState
|
||||
return state.copy(
|
||||
sendingFooter = getSendingFooterText(),
|
||||
notifications = buildList {
|
||||
addTooHighNotification(feeUM.feeSelectorUM)
|
||||
addTooLowNotification(feeUM)
|
||||
}.toPersistentList(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addTooLowNotification(feeUM: FeeUM.Content) {
|
||||
val feeSelectorUM = feeUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||
val multipleFees = feeSelectorUM.fees as? TransactionFee.Choosable ?: return
|
||||
val minimumValue = multipleFees.minimum.amount.value ?: return
|
||||
val customAmount = feeSelectorUM.customValues.firstOrNull() ?: return
|
||||
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
|
||||
if (feeSelectorUM.selectedType == FeeType.Custom && minimumValue > customValue) {
|
||||
add(NotificationUM.Warning.FeeTooLow)
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.NoticeTransactionDelays(
|
||||
categoryName = analyticsCategoryName,
|
||||
token = cryptoCurrency.symbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSendingFooterText(): TextReference {
|
||||
val feeUM = feeUM as? FeeUM.Content
|
||||
val fee = (feeUM?.feeSelectorUM as? FeeSelectorUM.Content)?.selectedFee ?: return TextReference.EMPTY
|
||||
|
||||
val fiatFeeValue = feeUM.rate?.let { fee.amount.value?.multiply(it) }
|
||||
|
||||
val fiatFee = formatFooterFiatFee(
|
||||
amount = fee.amount.copy(value = fiatFeeValue),
|
||||
isFeeConvertibleToFiat = feeUM.isFeeConvertibleToFiat,
|
||||
isFeeApproximate = feeUM.isFeeApproximate,
|
||||
appCurrency = appCurrency,
|
||||
)
|
||||
|
||||
return if (feeUM.isTronToken && fee is Fee.Tron) {
|
||||
getTronTokenFeeSendingText(
|
||||
fee = fee,
|
||||
fiatFee = fiatFee,
|
||||
fiatSending = resourceReference(R.string.common_nft),
|
||||
)
|
||||
} else {
|
||||
resourceReference(
|
||||
id = if (feeUM.isFeeConvertibleToFiat) {
|
||||
R.string.send_summary_transaction_description
|
||||
} else {
|
||||
R.string.send_summary_transaction_description_no_fiat_fee
|
||||
},
|
||||
formatArgs = wrappedList(
|
||||
resourceReference(R.string.common_nft),
|
||||
fiatFee,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addTooHighNotification(feeSelectorUM: FeeSelectorUM) {
|
||||
if (feeSelectorUM !is FeeSelectorUM.Content) return
|
||||
|
||||
val (isFeeTooHigh, diff) = checkIfFeeTooHigh(feeSelectorUM)
|
||||
if (isFeeTooHigh) {
|
||||
add(NotificationUM.Warning.TooHigh(diff))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -8,25 +7,17 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.core.ui.utils.toTimeFormat
|
||||
import com.tangem.features.nft.component.NFTDetailsBlockComponent
|
||||
import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.tapHelp
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.notifications
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.DefaultSendNotificationsComponent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -39,7 +30,6 @@ internal fun NFTSendConfirmContent(
|
|||
nftSendUM: NFTSendUM,
|
||||
destinationBlockComponent: DefaultSendDestinationBlockComponent,
|
||||
nftDetailsBlockComponent: NFTDetailsBlockComponent,
|
||||
feeBlockComponent: SendFeeBlockComponent,
|
||||
feeSelectorBlockComponent: FeeSelectorBlockComponent,
|
||||
notificationsComponent: DefaultSendNotificationsComponent,
|
||||
notificationsUM: ImmutableList<NotificationUM>,
|
||||
|
|
@ -50,10 +40,8 @@ internal fun NFTSendConfirmContent(
|
|||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
) {
|
||||
blocks(
|
||||
nftSendUM = nftSendUM,
|
||||
destinationBlockComponent = destinationBlockComponent,
|
||||
nftDetailsBlockComponent = nftDetailsBlockComponent,
|
||||
feeBlockComponent = feeBlockComponent,
|
||||
feeSelectorBlockComponent = feeSelectorBlockComponent,
|
||||
)
|
||||
if (confirmUM != null) {
|
||||
|
|
@ -73,49 +61,19 @@ internal fun NFTSendConfirmContent(
|
|||
}
|
||||
|
||||
private fun LazyListScope.blocks(
|
||||
nftSendUM: NFTSendUM,
|
||||
destinationBlockComponent: DefaultSendDestinationBlockComponent,
|
||||
nftDetailsBlockComponent: NFTDetailsBlockComponent,
|
||||
feeBlockComponent: SendFeeBlockComponent,
|
||||
feeSelectorBlockComponent: FeeSelectorBlockComponent,
|
||||
) {
|
||||
item(key = BLOCKS_KEY) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
if (nftSendUM.isRedesignEnabled) {
|
||||
nftDetailsBlockComponent.Content(modifier = Modifier)
|
||||
destinationBlockComponent.Content(modifier = Modifier)
|
||||
feeSelectorBlockComponent.Content(
|
||||
modifier = Modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
} else {
|
||||
TransactionDoneTitleAnimated(nftSendUM = nftSendUM)
|
||||
destinationBlockComponent.Content(modifier = Modifier)
|
||||
nftDetailsBlockComponent.Content(modifier = Modifier)
|
||||
feeBlockComponent.Content(modifier = Modifier)
|
||||
}
|
||||
nftDetailsBlockComponent.Content(modifier = Modifier)
|
||||
destinationBlockComponent.Content(modifier = Modifier)
|
||||
feeSelectorBlockComponent.Content(
|
||||
modifier = Modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TransactionDoneTitleAnimated(nftSendUM: NFTSendUM) {
|
||||
AnimatedVisibility(
|
||||
visible = nftSendUM.confirmUM is ConfirmUM.Success,
|
||||
modifier = Modifier.padding(vertical = 12.dp),
|
||||
) {
|
||||
val wrappedConfirmUM = remember(this) { nftSendUM.confirmUM as ConfirmUM.Success }
|
||||
TransactionDoneTitle(
|
||||
title = resourceReference(R.string.sent_transaction_sent_title),
|
||||
subtitle = resourceReference(
|
||||
R.string.send_date_format,
|
||||
wrappedList(
|
||||
wrappedConfirmUM.transactionDate.toTimeFormat(DateTimeFormatters.dateFormatter),
|
||||
wrappedConfirmUM.transactionDate.toTimeFormat(),
|
||||
),
|
||||
),
|
||||
modifier = Modifier.padding(vertical = 12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +32,6 @@ import com.tangem.domain.transaction.usecase.GetFeeUseCase
|
|||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.nft.entity.NFTSendSuccessTrigger
|
||||
import com.tangem.features.send.v2.api.NFTSendComponent
|
||||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponent
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
|
||||
|
|
@ -43,8 +42,6 @@ import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
|||
import com.tangem.features.send.v2.sendnft.confirm.NFTSendConfirmComponent
|
||||
import com.tangem.features.send.v2.sendnft.success.NFTSendSuccessComponent
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -52,7 +49,6 @@ import javax.inject.Inject
|
|||
import kotlin.properties.Delegates
|
||||
|
||||
internal interface SendNFTComponentCallback :
|
||||
SendFeeComponent.ModelCallback,
|
||||
SendDestinationComponent.ModelCallback,
|
||||
NFTSendConfirmComponent.ModelCallback
|
||||
|
||||
|
|
@ -74,7 +70,6 @@ internal class NFTSendModel @Inject constructor(
|
|||
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val alertFactory: SendConfirmAlertFactory,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
private val nftSendSuccessTrigger: NFTSendSuccessTrigger,
|
||||
) : Model(), SendNFTComponentCallback, NFTSendSuccessComponent.ModelCallback {
|
||||
|
||||
|
|
@ -116,10 +111,6 @@ internal class NFTSendModel @Inject constructor(
|
|||
uiState.update { it.copy(destinationUM = destinationUM) }
|
||||
}
|
||||
|
||||
override fun onFeeResult(feeUM: FeeUM) {
|
||||
uiState.update { it.copy(feeUM = feeUM) }
|
||||
}
|
||||
|
||||
override fun onBackClick() {
|
||||
if (currentRouteFlow.value == ConfirmSuccess) {
|
||||
modelScope.launch {
|
||||
|
|
@ -254,10 +245,8 @@ internal class NFTSendModel @Inject constructor(
|
|||
|
||||
private fun initialState(): NFTSendUM = NFTSendUM(
|
||||
destinationUM = DestinationUM.Empty(),
|
||||
feeUM = FeeUM.Empty(),
|
||||
feeSelectorUM = FeeSelectorUM.Loading,
|
||||
confirmUM = ConfirmUM.Empty,
|
||||
navigationUM = NavigationUM.Empty,
|
||||
isRedesignEnabled = sendFeatureToggles.isNFTSendRedesignEnabled,
|
||||
)
|
||||
}
|
||||
|
|
@ -4,13 +4,10 @@ import com.tangem.common.ui.navigationButtons.NavigationUM
|
|||
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
|
||||
internal data class NFTSendUM(
|
||||
val destinationUM: DestinationUM,
|
||||
val feeUM: FeeUM,
|
||||
val feeSelectorUM: FeeSelectorUM,
|
||||
val confirmUM: ConfirmUM,
|
||||
val navigationUM: NavigationUM,
|
||||
val isRedesignEnabled: Boolean,
|
||||
)
|
||||
|
|
@ -5,7 +5,6 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.ui.AmountBlock
|
||||
import com.tangem.common.ui.amountScreen.ui.AmountBlockV2
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
|
|
@ -38,21 +37,12 @@ internal class SendAmountBlockComponent(
|
|||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
val isClickEnabled by params.blockClickEnableFlow.collectAsStateWithLifecycle()
|
||||
|
||||
if (params.isRedesignEnabled) {
|
||||
AmountBlockV2(
|
||||
amountState = state,
|
||||
isClickDisabled = !isClickEnabled,
|
||||
isEditingDisabled = params.predefinedValues is PredefinedValues.Content.Deeplink,
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
AmountBlock(
|
||||
amountState = state,
|
||||
isClickDisabled = !isClickEnabled,
|
||||
isEditingDisabled = params.predefinedValues is PredefinedValues.Content.Deeplink,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
AmountBlockV2(
|
||||
amountState = state,
|
||||
isClickDisabled = !isClickEnabled,
|
||||
isEditingDisabled = params.predefinedValues is PredefinedValues.Content.Deeplink,
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,6 @@ internal sealed class SendAmountComponentParams {
|
|||
abstract val userWalletId: UserWalletId
|
||||
abstract val appCurrency: AppCurrency
|
||||
abstract val predefinedValues: PredefinedValues
|
||||
abstract val isRedesignEnabled: Boolean
|
||||
abstract val cryptoCurrency: CryptoCurrency
|
||||
abstract val cryptoCurrencyStatusFlow: StateFlow<CryptoCurrencyStatus>
|
||||
abstract val isBalanceHidingFlow: StateFlow<Boolean>
|
||||
|
|
@ -31,7 +30,6 @@ internal sealed class SendAmountComponentParams {
|
|||
override val userWalletId: UserWalletId,
|
||||
override val appCurrency: AppCurrency,
|
||||
override val predefinedValues: PredefinedValues,
|
||||
override val isRedesignEnabled: Boolean,
|
||||
override val cryptoCurrency: CryptoCurrency,
|
||||
override val cryptoCurrencyStatusFlow: StateFlow<CryptoCurrencyStatus>,
|
||||
override val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
|
|
@ -46,7 +44,6 @@ internal sealed class SendAmountComponentParams {
|
|||
override val appCurrency: AppCurrency,
|
||||
override val userWalletId: UserWalletId,
|
||||
override val predefinedValues: PredefinedValues,
|
||||
override val isRedesignEnabled: Boolean,
|
||||
override val cryptoCurrency: CryptoCurrency,
|
||||
override val cryptoCurrencyStatusFlow: StateFlow<CryptoCurrencyStatus>,
|
||||
override val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.WrappedList
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
|
|
@ -29,7 +28,6 @@ import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
|
|||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
import com.tangem.features.send.v2.api.entity.PredefinedValues
|
||||
import com.tangem.features.send.v2.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents
|
||||
import com.tangem.features.send.v2.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents.SelectedCurrencyType
|
||||
|
|
@ -39,8 +37,6 @@ import com.tangem.features.send.v2.impl.R
|
|||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateListener
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeData
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadTrigger
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.isNullOrZero
|
||||
|
|
@ -60,9 +56,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
private val sendAmountReduceListener: SendAmountReduceListener,
|
||||
private val sendAmountUpdateListener: SendAmountUpdateListener,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val feeReloadTrigger: SendFeeReloadTrigger,
|
||||
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val rampStateManager: RampStateManager,
|
||||
|
|
@ -129,7 +123,6 @@ internal class SendAmountModel @Inject constructor(
|
|||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxEnterAmount = maxAmountBoundary,
|
||||
appCurrency = appCurrency,
|
||||
isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled,
|
||||
isBalanceHidden = params.isBalanceHidingFlow.value,
|
||||
),
|
||||
)
|
||||
|
|
@ -166,7 +159,6 @@ internal class SendAmountModel @Inject constructor(
|
|||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxEnterAmount = maxAmountBoundary,
|
||||
appCurrency = appCurrency,
|
||||
isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled,
|
||||
isBalanceHidden = params.isBalanceHidingFlow.value,
|
||||
),
|
||||
)
|
||||
|
|
@ -186,7 +178,6 @@ internal class SendAmountModel @Inject constructor(
|
|||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxEnterAmount = maxAmountBoundary,
|
||||
iconStateConverter = CryptoCurrencyToIconStateConverter(),
|
||||
isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled,
|
||||
isBalanceHidden = params.isBalanceHidingFlow.value,
|
||||
).convert(
|
||||
AmountParameters(
|
||||
|
|
@ -312,14 +303,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
value = reduceTo,
|
||||
),
|
||||
)
|
||||
val amountValue = (uiState.value as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
|
||||
if (uiState.value.isRedesignEnabled) {
|
||||
feeSelectorReloadTrigger.triggerUpdate()
|
||||
} else {
|
||||
feeReloadTrigger.triggerUpdate(
|
||||
feeData = SendFeeData(amount = amountValue),
|
||||
)
|
||||
}
|
||||
feeSelectorReloadTrigger.triggerUpdate()
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
|
@ -334,15 +318,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
value = reduceByData,
|
||||
),
|
||||
)
|
||||
|
||||
val amountValue = (uiState.value as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
|
||||
if (uiState.value.isRedesignEnabled) {
|
||||
feeSelectorReloadTrigger.triggerUpdate()
|
||||
} else {
|
||||
feeReloadTrigger.triggerUpdate(
|
||||
feeData = SendFeeData(amount = amountValue),
|
||||
)
|
||||
}
|
||||
feeSelectorReloadTrigger.triggerUpdate()
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
|
@ -384,7 +360,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
NavigationUM.Content(
|
||||
title = resourceReference(R.string.send_amount_label),
|
||||
subtitle = null,
|
||||
backIconRes = if (route.isEditMode || params.isRedesignEnabled) {
|
||||
backIconRes = if (route.isEditMode) {
|
||||
R.drawable.ic_back_24
|
||||
} else {
|
||||
R.drawable.ic_close_24
|
||||
|
|
@ -402,20 +378,6 @@ internal class SendAmountModel @Inject constructor(
|
|||
params.callback.onNextClick()
|
||||
},
|
||||
),
|
||||
prevButton = if (params.isRedesignEnabled) {
|
||||
null
|
||||
} else {
|
||||
NavigationButton(
|
||||
textReference = TextReference.EMPTY,
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
isEnabled = true,
|
||||
onClick = {
|
||||
saveResult()
|
||||
params.callback.onBackClick()
|
||||
},
|
||||
).takeIf { route.isEditMode.not() }
|
||||
},
|
||||
secondaryPairButtonsUM = null,
|
||||
),
|
||||
)
|
||||
}.launchIn(modelScope)
|
||||
|
|
@ -428,7 +390,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
// Allowed only on networks without tx extras (e.i. memo and destination tag)
|
||||
val isExtrasSupported = cryptoCurrencyStatus.currency.network.transactionExtrasType.isTxExtrasSupported()
|
||||
isSendWithSwapAvailable.update {
|
||||
isAvailableForSwap && isMultiCurrency && sendFeatureToggles.isSendWithSwapEnabled && !isExtrasSupported
|
||||
isAvailableForSwap && isMultiCurrency && !isExtrasSupported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,6 @@ internal class DefaultSendDestinationBlockComponent @AssistedInject constructor(
|
|||
destinationUM = state,
|
||||
isClickDisabled = !isClickEnabled,
|
||||
isEditingDisabled = params.predefinedValues is PredefinedValues.Content.Deeplink,
|
||||
isRedesignEnabled = (params.state as? DestinationUM.Content)?.isRedesignEnabled ?: false,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase
|
|||
import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetFixedTxHistoryItemsUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.SendScreenSource
|
||||
import com.tangem.features.send.v2.api.entity.PredefinedValues
|
||||
|
|
@ -66,7 +65,6 @@ internal class SendDestinationModel @Inject constructor(
|
|||
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
|
||||
private val parseQrCodeUseCase: ParseQrCodeUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
) : Model(), SendDestinationClickIntents {
|
||||
private val params: SendDestinationComponentParams = paramsContainer.require()
|
||||
|
||||
|
|
@ -92,7 +90,6 @@ internal class SendDestinationModel @Inject constructor(
|
|||
_uiState.update(
|
||||
SendDestinationInitialStateTransformer(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled,
|
||||
isInitialized = true,
|
||||
),
|
||||
)
|
||||
|
|
@ -301,12 +298,11 @@ internal class SendDestinationModel @Inject constructor(
|
|||
flow2 = params.currentRoute,
|
||||
transform = { state, route -> state to route },
|
||||
).onEach { (state, route) ->
|
||||
val isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled
|
||||
params.callback.onNavigationResult(
|
||||
NavigationUM.Content(
|
||||
title = params.title,
|
||||
subtitle = null,
|
||||
backIconRes = if (route.isEditMode || isRedesignEnabled) {
|
||||
backIconRes = if (route.isEditMode) {
|
||||
R.drawable.ic_back_24
|
||||
} else {
|
||||
R.drawable.ic_close_24
|
||||
|
|
@ -325,16 +321,6 @@ internal class SendDestinationModel @Inject constructor(
|
|||
}
|
||||
params.callback.onBackClick()
|
||||
},
|
||||
additionalIconRes = if (isRedesignEnabled) {
|
||||
null
|
||||
} else {
|
||||
R.drawable.ic_qrcode_scan_24
|
||||
},
|
||||
additionalIconClick = if (isRedesignEnabled) {
|
||||
null
|
||||
} else {
|
||||
::onQrCodeScanClick
|
||||
},
|
||||
primaryButton = NavigationButton(
|
||||
textReference = if (route.isEditMode) {
|
||||
resourceReference(R.string.common_continue)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import com.tangem.utils.transformer.Transformer
|
|||
|
||||
internal class SendDestinationInitialStateTransformer(
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
val isRedesignEnabled: Boolean,
|
||||
val isInitialized: Boolean = false,
|
||||
) : Transformer<DestinationUM> {
|
||||
override fun transform(prevState: DestinationUM): DestinationUM {
|
||||
|
|
@ -59,7 +58,6 @@ internal class SendDestinationInitialStateTransformer(
|
|||
recent = loadingListState(RECENT_KEY_TAG, RECENT_DEFAULT_COUNT),
|
||||
networkName = cryptoCurrency.network.name,
|
||||
isValidating = false,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
isRecentHidden = false,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -17,7 +16,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import com.tangem.core.ui.components.icons.identicon.IdentIcon
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -33,7 +31,6 @@ internal fun DestinationBlock(
|
|||
destinationUM: DestinationUM,
|
||||
isClickDisabled: Boolean,
|
||||
isEditingDisabled: Boolean,
|
||||
isRedesignEnabled: Boolean,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
if (destinationUM !is DestinationUM.Content) return
|
||||
|
|
@ -46,72 +43,9 @@ internal fun DestinationBlock(
|
|||
.clickable(enabled = !isClickDisabled && !isEditingDisabled, onClick = onClick)
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
if (isRedesignEnabled) {
|
||||
AddressWithMemoBlock(
|
||||
address = destinationUM.addressTextField,
|
||||
memo = destinationUM.memoTextField,
|
||||
)
|
||||
} else {
|
||||
AddressBlock(destinationUM.addressTextField)
|
||||
MemoBlock(destinationUM.memoTextField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddressBlock(address: DestinationTextFieldUM.RecipientAddress) {
|
||||
Text(
|
||||
text = address.label.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
IdentIcon(
|
||||
address = address.value,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size36)
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius18))
|
||||
.background(TangemTheme.colors.background.tertiary),
|
||||
)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = address.value,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
val blockchainAddress = address.briefBlockchainAddress
|
||||
if (!blockchainAddress.isNullOrBlank()) {
|
||||
Text(
|
||||
text = blockchainAddress,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MemoBlock(memo: DestinationTextFieldUM.RecipientMemo?) {
|
||||
if (memo != null && memo.value.isNotBlank()) {
|
||||
HorizontalDivider(
|
||||
color = TangemTheme.colors.icon.inactive,
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
Text(
|
||||
text = memo.label.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
Text(
|
||||
text = memo.value,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
|
||||
AddressWithMemoBlock(
|
||||
address = destinationUM.addressTextField,
|
||||
memo = destinationUM.memoTextField,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -176,7 +110,6 @@ private fun DestinationBlockPreview(
|
|||
destinationUM = state,
|
||||
isClickDisabled = false,
|
||||
isEditingDisabled = false,
|
||||
isRedesignEnabled = state.isRedesignEnabled,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -210,7 +143,6 @@ private class DestinationBlockPreviewProvider : PreviewParameterProvider<Destina
|
|||
networkName = "Ethereum",
|
||||
isValidating = false,
|
||||
isInitialized = true,
|
||||
isRedesignEnabled = false,
|
||||
isRecentHidden = false,
|
||||
)
|
||||
|
||||
|
|
@ -223,13 +155,11 @@ private class DestinationBlockPreviewProvider : PreviewParameterProvider<Destina
|
|||
blockchainAddress = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE",
|
||||
),
|
||||
),
|
||||
previewItem.copy(isRedesignEnabled = true),
|
||||
previewItem.copy(
|
||||
addressTextField = previewItem.addressTextField.copy(
|
||||
value = "vitalik.eth",
|
||||
blockchainAddress = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE",
|
||||
),
|
||||
isRedesignEnabled = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -62,12 +62,10 @@ internal fun SendDestinationContent(
|
|||
isValidating = isValidating,
|
||||
onAddressChange = clickIntents::onRecipientAddressValueChange,
|
||||
onQrCodeClick = clickIntents::onQrCodeScanClick,
|
||||
isRedesignEnabled = state.isRedesignEnabled,
|
||||
)
|
||||
memoField(
|
||||
memoField = memoField,
|
||||
onMemoChange = clickIntents::onRecipientMemoValueChange,
|
||||
isRedesignEnabled = state.isRedesignEnabled,
|
||||
)
|
||||
listHeaderItem(
|
||||
titleRes = R.string.send_recipient_wallets_title,
|
||||
|
|
@ -117,7 +115,6 @@ private fun LazyListScope.addressItem(
|
|||
isValidating: Boolean,
|
||||
onAddressChange: (String, EnterAddressSource) -> Unit,
|
||||
onQrCodeClick: () -> Unit,
|
||||
isRedesignEnabled: Boolean,
|
||||
) {
|
||||
item(key = ADDRESS_FIELD_KEY) {
|
||||
FooterContainer(
|
||||
|
|
@ -130,7 +127,6 @@ private fun LazyListScope.addressItem(
|
|||
onValueChange = { onAddressChange(it, EnterAddressSource.InputField) },
|
||||
onPasteClick = { onAddressChange(it, EnterAddressSource.PasteButton) },
|
||||
onQrCodeClick = onQrCodeClick,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
isError = isError,
|
||||
isLoading = isValidating,
|
||||
error = address.error,
|
||||
|
|
@ -149,7 +145,6 @@ private fun LazyListScope.addressItem(
|
|||
private fun LazyListScope.memoField(
|
||||
memoField: DestinationTextFieldUM.RecipientMemo?,
|
||||
onMemoChange: (String, Boolean) -> Unit,
|
||||
isRedesignEnabled: Boolean,
|
||||
) {
|
||||
if (memoField != null) {
|
||||
item(key = MEMO_FIELD_KEY) {
|
||||
|
|
@ -158,24 +153,20 @@ private fun LazyListScope.memoField(
|
|||
value = memoField.value,
|
||||
label = memoField.label,
|
||||
placeholder = placeholder,
|
||||
footer = if (isRedesignEnabled) {
|
||||
annotatedReference(
|
||||
buildAnnotatedString {
|
||||
append(stringResourceSafe(R.string.send_recipient_memo_footer_v2))
|
||||
append("\n")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Medium)) {
|
||||
appendColored(
|
||||
text = stringResourceSafe(
|
||||
R.string.send_recipient_memo_footer_v2_highlighted,
|
||||
),
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
resourceReference(R.string.send_recipient_memo_footer)
|
||||
},
|
||||
footer = annotatedReference(
|
||||
buildAnnotatedString {
|
||||
append(stringResourceSafe(R.string.send_recipient_memo_footer_v2))
|
||||
append("\n")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Medium)) {
|
||||
appendColored(
|
||||
text = stringResourceSafe(
|
||||
R.string.send_recipient_memo_footer_v2_highlighted,
|
||||
),
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
},
|
||||
),
|
||||
onValueChange = { onMemoChange(it, false) },
|
||||
onPasteClick = { onMemoChange(it, true) },
|
||||
modifier = Modifier.padding(top = 20.dp),
|
||||
|
|
@ -184,7 +175,6 @@ private fun LazyListScope.memoField(
|
|||
error = memoField.error,
|
||||
isReadOnly = !memoField.isEnabled,
|
||||
isValuePasted = memoField.isValuePasted,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ internal fun TextFieldWithPaste(
|
|||
label: TextReference,
|
||||
onValueChange: (String) -> Unit,
|
||||
onPasteClick: (String) -> Unit,
|
||||
isRedesignEnabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
footer: TextReference? = null,
|
||||
labelStyle: TextStyle = TangemTheme.typography.body2,
|
||||
|
|
@ -98,16 +97,8 @@ internal fun TextFieldWithPaste(
|
|||
PasteButton(
|
||||
isPasteButtonVisible = value.isBlank(),
|
||||
onClick = onPasteClick,
|
||||
backgroundColorEnabled = if (isRedesignEnabled) {
|
||||
TangemTheme.colors.button.secondary
|
||||
} else {
|
||||
TangemTheme.colors.button.primary
|
||||
},
|
||||
textColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors.text.primary1
|
||||
} else {
|
||||
TangemTheme.colors.text.primary2
|
||||
},
|
||||
backgroundColorEnabled = TangemTheme.colors.button.secondary,
|
||||
textColor = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.FeeBlock
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
internal class SendFeeBlockComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: SendFeeComponentParams.FeeBlockParams,
|
||||
val onResult: (FeeUM) -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: SendFeeModel = getOrCreateModel(params = params)
|
||||
|
||||
init {
|
||||
model.uiState.onEach {
|
||||
onResult(it)
|
||||
}.launchIn(componentScope)
|
||||
}
|
||||
|
||||
fun updateState(state: FeeUM) = model.updateState(state)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state = model.uiState.collectAsStateWithLifecycle()
|
||||
val isClickEnabled = params.blockClickEnableFlow.collectAsStateWithLifecycle()
|
||||
FeeBlock(
|
||||
feeUM = state.value,
|
||||
isClickEnabled = isClickEnabled.value,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.common.ui.navigationButtons.NavigationModelCallback
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeModel
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.SendFeeContent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
|
||||
internal class SendFeeComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: SendFeeComponentParams.FeeParams,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: SendFeeModel = getOrCreateModel(params = params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state = model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
SendFeeContent(state = state.value, clickIntents = model)
|
||||
}
|
||||
|
||||
interface ModelCallback : NavigationModelCallback {
|
||||
fun onFeeResult(feeUM: FeeUM)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal sealed class SendFeeComponentParams {
|
||||
|
||||
abstract val state: FeeUM
|
||||
abstract val analyticsCategoryName: String
|
||||
abstract val userWallet: UserWallet
|
||||
abstract val cryptoCurrencyStatus: CryptoCurrencyStatus
|
||||
abstract val feeCryptoCurrencyStatus: CryptoCurrencyStatus
|
||||
abstract val appCurrency: AppCurrency
|
||||
abstract val sendAmount: BigDecimal
|
||||
abstract val destinationAddress: String
|
||||
abstract val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>
|
||||
abstract val analyticsSendSource: CommonSendAnalyticEvents.CommonSendSource
|
||||
|
||||
data class FeeParams(
|
||||
override val state: FeeUM,
|
||||
override val analyticsCategoryName: String,
|
||||
override val userWallet: UserWallet,
|
||||
override val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
override val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
override val appCurrency: AppCurrency,
|
||||
override val sendAmount: BigDecimal,
|
||||
override val destinationAddress: String,
|
||||
override val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>,
|
||||
override val analyticsSendSource: CommonSendAnalyticEvents.CommonSendSource,
|
||||
val currentRoute: Flow<CommonSendRoute.Fee>,
|
||||
val callback: SendFeeComponent.ModelCallback,
|
||||
) : SendFeeComponentParams()
|
||||
|
||||
data class FeeBlockParams(
|
||||
override val state: FeeUM,
|
||||
override val analyticsCategoryName: String,
|
||||
override val userWallet: UserWallet,
|
||||
override val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
override val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
override val appCurrency: AppCurrency,
|
||||
override val sendAmount: BigDecimal,
|
||||
override val destinationAddress: String,
|
||||
override val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>,
|
||||
override val analyticsSendSource: CommonSendAnalyticEvents.CommonSendSource,
|
||||
val blockClickEnableFlow: StateFlow<Boolean>,
|
||||
) : SendFeeComponentParams()
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class SendFeeData(
|
||||
val amount: BigDecimal?,
|
||||
val destinationAddress: String? = null,
|
||||
)
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
interface SendFeeReloadTrigger {
|
||||
|
||||
/** Trigger fee update */
|
||||
suspend fun triggerUpdate(feeData: SendFeeData)
|
||||
}
|
||||
|
||||
interface SendFeeReloadListener {
|
||||
/** Flow triggers fee reload */
|
||||
val reloadTriggerFlow: Flow<SendFeeData>
|
||||
}
|
||||
|
||||
interface SendFeeCheckReloadTrigger {
|
||||
/** Trigger return callback with check result */
|
||||
suspend fun callbackCheckResult(isSuccess: Boolean)
|
||||
|
||||
/** Trigger fee check reload */
|
||||
suspend fun triggerCheckUpdate()
|
||||
}
|
||||
|
||||
interface SendFeeCheckReloadListener {
|
||||
/**
|
||||
* Flow triggers fee check reload before transaction.
|
||||
* Usually after significant time after fee was updated last time
|
||||
*/
|
||||
val checkReloadTriggerFlow: Flow<Unit>
|
||||
|
||||
/** Flow return result of fee check update */
|
||||
val checkReloadResultFlow: Flow<Boolean>
|
||||
}
|
||||
|
||||
@Singleton
|
||||
internal class DefaultSendFeeReloadTrigger @Inject constructor() :
|
||||
SendFeeReloadTrigger,
|
||||
SendFeeReloadListener,
|
||||
SendFeeCheckReloadTrigger,
|
||||
SendFeeCheckReloadListener {
|
||||
|
||||
private val _reloadTriggerFlow = MutableSharedFlow<SendFeeData>()
|
||||
override val reloadTriggerFlow = _reloadTriggerFlow.asSharedFlow()
|
||||
|
||||
private val _checkReloadTriggerFlow = MutableSharedFlow<Unit>()
|
||||
override val checkReloadTriggerFlow = _checkReloadTriggerFlow.asSharedFlow()
|
||||
|
||||
private val _checkReloadResultFlow = MutableSharedFlow<Boolean>()
|
||||
override val checkReloadResultFlow = _checkReloadResultFlow.asSharedFlow()
|
||||
|
||||
override suspend fun triggerUpdate(feeData: SendFeeData) {
|
||||
_reloadTriggerFlow.emit(feeData)
|
||||
}
|
||||
|
||||
override suspend fun triggerCheckUpdate() {
|
||||
_checkReloadTriggerFlow.emit(Unit)
|
||||
}
|
||||
|
||||
override suspend fun callbackCheckResult(isSuccess: Boolean) {
|
||||
_checkReloadResultFlow.emit(isSuccess)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.di
|
||||
|
||||
import com.tangem.features.send.v2.subcomponents.fee.*
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Module
|
||||
internal interface SendFeeModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun provideSendFeeReloadTrigger(impl: DefaultSendFeeReloadTrigger): SendFeeReloadTrigger
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun provideSendFeeReloadListener(impl: DefaultSendFeeReloadTrigger): SendFeeReloadListener
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun provideSendFeeCheckReloadTrigger(impl: DefaultSendFeeReloadTrigger): SendFeeCheckReloadTrigger
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun provideSendFeeCheckReloadListener(impl: DefaultSendFeeReloadTrigger): SendFeeCheckReloadListener
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model
|
||||
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
||||
private val FEE_MAX_DIFF = BigDecimal("5")
|
||||
|
||||
/**
|
||||
* Check and calculates subtracted amount
|
||||
*/
|
||||
internal fun checkAndCalculateSubtractedAmount(
|
||||
isAmountSubtractAvailable: Boolean,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
amountValue: BigDecimal,
|
||||
feeValue: BigDecimal,
|
||||
reduceAmountBy: BigDecimal,
|
||||
): BigDecimal {
|
||||
val balance = cryptoCurrencyStatus.value.amount ?: return amountValue
|
||||
val isFeeCoverage = checkFeeCoverage(
|
||||
isSubtractAvailable = isAmountSubtractAvailable,
|
||||
balance = balance,
|
||||
amountValue = amountValue,
|
||||
feeValue = feeValue,
|
||||
reduceAmountBy = reduceAmountBy,
|
||||
)
|
||||
return if (isFeeCoverage) {
|
||||
balance.minus(reduceAmountBy).minus(feeValue)
|
||||
} else {
|
||||
amountValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if custom fee is too high
|
||||
*/
|
||||
internal fun checkIfFeeTooHigh(feeSelectorState: FeeSelectorUM.Content): Pair<Boolean, String> {
|
||||
val defaultResult = false to ""
|
||||
val multipleFees = feeSelectorState.fees as? TransactionFee.Choosable ?: return defaultResult
|
||||
val highValue = multipleFees.priority.amount.value ?: return defaultResult
|
||||
val customAmount = feeSelectorState.customValues.firstOrNull() ?: return defaultResult
|
||||
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
|
||||
val diff = if (highValue > BigDecimal.ZERO) {
|
||||
customValue / highValue
|
||||
} else {
|
||||
BigDecimal.ZERO
|
||||
}
|
||||
val isFeeTooHigh = feeSelectorState.selectedType == FeeType.Custom && diff > FEE_MAX_DIFF
|
||||
return isFeeTooHigh to diff.parseBigDecimal(0, RoundingMode.HALF_UP)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if custom fee is too low
|
||||
*/
|
||||
internal fun checkIfFeeTooLow(feeSelectorUM: FeeSelectorUM.Content): Boolean {
|
||||
val multipleFees = feeSelectorUM.fees as? TransactionFee.Choosable ?: return false
|
||||
val minimumValue = multipleFees.minimum.amount.value ?: return false
|
||||
val customAmount = feeSelectorUM.customValues.firstOrNull() ?: return false
|
||||
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
|
||||
|
||||
return feeSelectorUM.selectedType == FeeType.Custom && minimumValue > customValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if sending amount with fee is greater than balance
|
||||
*/
|
||||
internal fun checkFeeCoverage(
|
||||
isSubtractAvailable: Boolean,
|
||||
balance: BigDecimal,
|
||||
amountValue: BigDecimal,
|
||||
feeValue: BigDecimal,
|
||||
reduceAmountBy: BigDecimal?,
|
||||
): Boolean {
|
||||
if (!isSubtractAvailable) return false
|
||||
val reducedBy = balance - (reduceAmountBy ?: BigDecimal.ZERO)
|
||||
return reducedBy < amountValue + feeValue && reducedBy > feeValue && reducedBy >= amountValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if fee exceeds fee paid currency balance
|
||||
*/
|
||||
fun checkExceedBalance(feeBalance: BigDecimal?, feeAmount: BigDecimal?): Boolean {
|
||||
return feeAmount == null || feeBalance == null || feeAmount.isZero() || feeAmount > feeBalance
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model
|
||||
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class SendFeeAlertFactory @Inject constructor(
|
||||
private val messageSender: UiMessageSender,
|
||||
) {
|
||||
|
||||
fun checkAndShowAlerts(feeSelectorUM: FeeSelectorUM.Content, onConfirmClick: () -> Unit) {
|
||||
val showFeeTooLow = checkAndShowFeeTooLow(feeSelectorUM, onConfirmClick)
|
||||
val showFeeTooHigh = checkAndShowFeeTooHigh(feeSelectorUM, onConfirmClick)
|
||||
|
||||
if (!showFeeTooLow && !showFeeTooHigh) {
|
||||
onConfirmClick()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if custom fee is too low
|
||||
*/
|
||||
private fun checkAndShowFeeTooLow(feeSelectorUM: FeeSelectorUM.Content, onConfirmClick: () -> Unit): Boolean {
|
||||
val isFeeTooLow = checkIfFeeTooLow(feeSelectorUM)
|
||||
if (isFeeTooLow) {
|
||||
messageSender.send(
|
||||
DialogMessage(
|
||||
message = resourceReference(id = R.string.send_alert_fee_too_low_text),
|
||||
dismissOnFirstAction = true,
|
||||
firstActionBuilder = {
|
||||
EventMessageAction(
|
||||
title = resourceReference(R.string.common_continue),
|
||||
onClick = onConfirmClick,
|
||||
)
|
||||
},
|
||||
secondActionBuilder = { cancelAction() },
|
||||
),
|
||||
)
|
||||
}
|
||||
return isFeeTooLow
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if custom fee is too high
|
||||
*/
|
||||
private fun checkAndShowFeeTooHigh(feeSelectorUM: FeeSelectorUM.Content, onConfirmClick: () -> Unit): Boolean {
|
||||
val (isFeeTooHigh, diff) = checkIfFeeTooHigh(feeSelectorUM)
|
||||
if (isFeeTooHigh) {
|
||||
messageSender.send(
|
||||
DialogMessage(
|
||||
message = resourceReference(
|
||||
id = R.string.send_alert_fee_too_high_text,
|
||||
formatArgs = wrappedList(diff),
|
||||
),
|
||||
dismissOnFirstAction = true,
|
||||
firstActionBuilder = {
|
||||
EventMessageAction(
|
||||
title = resourceReference(R.string.common_continue),
|
||||
onClick = onConfirmClick,
|
||||
)
|
||||
},
|
||||
secondActionBuilder = { cancelAction() },
|
||||
),
|
||||
)
|
||||
}
|
||||
return isFeeTooHigh
|
||||
}
|
||||
|
||||
fun getFeeUpdatedAlert(newFee: TransactionFee, feeUM: FeeUM, proceedAction: () -> Unit, stopAction: () -> Unit) {
|
||||
if (feeUM !is FeeUM.Content) return
|
||||
val feeSelectorUM = feeUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||
val newFee = when (newFee) {
|
||||
is TransactionFee.Single -> newFee.normal
|
||||
is TransactionFee.Choosable -> {
|
||||
when (feeSelectorUM.selectedType) {
|
||||
FeeType.Slow -> newFee.minimum
|
||||
FeeType.Market -> newFee.normal
|
||||
FeeType.Fast -> newFee.priority
|
||||
FeeType.Custom -> return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val newFeeValue = newFee.amount.value ?: BigDecimal.ZERO
|
||||
val oldFeeValue = feeSelectorUM.selectedFee?.amount?.value ?: BigDecimal.ZERO
|
||||
|
||||
if (newFeeValue > oldFeeValue) {
|
||||
messageSender.send(
|
||||
DialogMessage(
|
||||
message = resourceReference(id = R.string.send_notification_high_fee_title),
|
||||
dismissOnFirstAction = true,
|
||||
onDismissRequest = { stopAction() },
|
||||
firstActionBuilder = {
|
||||
EventMessageAction(
|
||||
title = resourceReference(R.string.common_understand),
|
||||
onClick = { stopAction(); onDismissRequest() },
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
} else {
|
||||
proceedAction()
|
||||
}
|
||||
}
|
||||
|
||||
fun getFeeUnreachableErrorState(onFeeReload: () -> Unit) {
|
||||
messageSender.send(
|
||||
DialogMessage(
|
||||
title = resourceReference(R.string.send_fee_unreachable_error_title),
|
||||
message = resourceReference(R.string.send_fee_unreachable_error_text),
|
||||
dismissOnFirstAction = true,
|
||||
firstActionBuilder = {
|
||||
EventMessageAction(
|
||||
title = resourceReference(R.string.warning_button_refresh),
|
||||
onClick = onFeeReload,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model
|
||||
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
|
||||
internal interface SendFeeClickIntents {
|
||||
|
||||
fun feeReload()
|
||||
|
||||
fun onFeeSelectorClick(feeType: FeeType)
|
||||
|
||||
fun onCustomFeeValueChange(index: Int, value: String)
|
||||
|
||||
fun onNonceChange(value: String)
|
||||
|
||||
fun onReadMoreClick()
|
||||
|
||||
fun onNextClick()
|
||||
}
|
||||
|
|
@ -1,295 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButton
|
||||
import com.tangem.common.ui.navigationButtons.NavigationUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
|
||||
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.analytics.CommonSendFeeAnalyticEvents
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadListener
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadListener
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.transformers.*
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.TangemLinks
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import com.tangem.utils.transformer.update
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class SendFeeModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val isFeeApproximateUseCase: IsFeeApproximateUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val sendFeeAlertFactory: SendFeeAlertFactory,
|
||||
private val feeReloadListener: SendFeeReloadListener,
|
||||
private val feeCheckReloadListener: SendFeeCheckReloadListener,
|
||||
private val feeCheckReloadTrigger: SendFeeCheckReloadTrigger,
|
||||
) : Model(), SendFeeClickIntents {
|
||||
|
||||
private val params: SendFeeComponentParams = paramsContainer.require()
|
||||
|
||||
private val _uiState = MutableStateFlow(params.state)
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
private val analyticsCategoryName = params.analyticsCategoryName
|
||||
private val appCurrency = params.appCurrency
|
||||
private val cryptoCurrencyStatus = params.cryptoCurrencyStatus
|
||||
private val feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus
|
||||
|
||||
private var feeJobHolder = JobHolder()
|
||||
|
||||
init {
|
||||
configFeeNavigation()
|
||||
subscribeOnFeeReloadTriggerUpdates()
|
||||
subscribeOnFeeCheckReloadTriggerUpdates()
|
||||
initialState()
|
||||
feeReload()
|
||||
}
|
||||
|
||||
fun updateState(state: FeeUM) {
|
||||
_uiState.value = state
|
||||
}
|
||||
|
||||
override fun feeReload() {
|
||||
modelScope.launch {
|
||||
val isShowLoading = (uiState.value as? FeeUM.Content)?.feeSelectorUM !is FeeSelectorUM.Content
|
||||
if (isShowLoading) {
|
||||
_uiState.update(SendFeeLoadingTransformer)
|
||||
}
|
||||
params.onLoadFee().fold(
|
||||
ifRight = {
|
||||
_uiState.update(
|
||||
SendFeeLoadedTransformer(
|
||||
fees = it,
|
||||
clickIntents = this@SendFeeModel,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
isFeeApproximate = isFeeApproximate(it.normal.amount.type),
|
||||
),
|
||||
)
|
||||
updateFeeNotifications()
|
||||
},
|
||||
ifLeft = { feeError ->
|
||||
if (isShowLoading) {
|
||||
_uiState.update(SendFeeFailedTransformer(feeError))
|
||||
}
|
||||
updateFeeNotifications()
|
||||
},
|
||||
)
|
||||
}.saveIn(feeJobHolder)
|
||||
}
|
||||
|
||||
override fun onFeeSelectorClick(feeType: FeeType) {
|
||||
_uiState.update(
|
||||
SendFeeSelectTransformer(
|
||||
feeType = feeType,
|
||||
clickIntents = this@SendFeeModel,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
updateFeeNotifications()
|
||||
if (feeType == FeeType.Custom) {
|
||||
analyticsEventHandler.send(
|
||||
CommonSendFeeAnalyticEvents.CustomFeeButtonClicked(categoryName = analyticsCategoryName),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCustomFeeValueChange(index: Int, value: String) {
|
||||
_uiState.update(
|
||||
SendFeeCustomValueChangeTransformer(
|
||||
index = index,
|
||||
value = value,
|
||||
clickIntents = this@SendFeeModel,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
updateFeeNotifications()
|
||||
}
|
||||
|
||||
override fun onNonceChange(value: String) {
|
||||
_uiState.update(
|
||||
SendFeeNonceChangeTransformer(value = value),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onReadMoreClick() {
|
||||
urlOpener.openUrl(TangemLinks.FEE_BLOG_LINK)
|
||||
}
|
||||
|
||||
override fun onNextClick() {
|
||||
val feeUM = uiState.value as? FeeUM.Content
|
||||
val feeSelectorUM = feeUM?.feeSelectorUM as? FeeSelectorUM.Content
|
||||
|
||||
if (feeSelectorUM != null) {
|
||||
sendFeeAlertFactory.checkAndShowAlerts(feeSelectorUM) {
|
||||
val isCustomFeeEdited =
|
||||
feeSelectorUM.selectedFee?.amount?.value != feeSelectorUM.fees.normal.amount.value
|
||||
if (feeSelectorUM.selectedType == FeeType.Custom && isCustomFeeEdited) {
|
||||
analyticsEventHandler.send(
|
||||
CommonSendFeeAnalyticEvents.GasPriceInserter(categoryName = analyticsCategoryName),
|
||||
)
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
CommonSendFeeAnalyticEvents.SelectedFee(
|
||||
categoryName = analyticsCategoryName,
|
||||
feeType = feeSelectorUM.selectedType.toAnalyticType(feeSelectorUM),
|
||||
source = params.analyticsSendSource,
|
||||
),
|
||||
)
|
||||
|
||||
if (feeSelectorUM.nonce != null) {
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.NonceInserted(
|
||||
categoryName = analyticsCategoryName,
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
blockchain = cryptoCurrencyStatus.currency.network.name,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
saveResult()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initialState() {
|
||||
if (uiState.value is FeeUM.Empty) {
|
||||
_uiState.update(
|
||||
SendFeeInitialStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
appCurrency = appCurrency,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnFeeReloadTriggerUpdates() {
|
||||
feeReloadListener.reloadTriggerFlow
|
||||
.onEach { feeReload() }
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnFeeCheckReloadTriggerUpdates() {
|
||||
feeCheckReloadListener.checkReloadTriggerFlow
|
||||
.onEach { checkLoadFee() }
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun saveResult() {
|
||||
_uiState.update(
|
||||
SendFeeCustomAutoFixTransformer(
|
||||
clickIntents = this@SendFeeModel,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
val params = params as? SendFeeComponentParams.FeeParams ?: return
|
||||
params.callback.onFeeResult(uiState.value)
|
||||
params.callback.onNextClick()
|
||||
}
|
||||
|
||||
private fun checkLoadFee() {
|
||||
modelScope.launch {
|
||||
params.onLoadFee().fold(
|
||||
ifRight = {
|
||||
sendFeeAlertFactory.getFeeUpdatedAlert(
|
||||
newFee = it,
|
||||
feeUM = uiState.value,
|
||||
proceedAction = {
|
||||
modelScope.launch {
|
||||
feeCheckReloadTrigger.callbackCheckResult(true)
|
||||
}
|
||||
},
|
||||
stopAction = {
|
||||
modelScope.launch {
|
||||
feeCheckReloadTrigger.callbackCheckResult(false)
|
||||
}
|
||||
},
|
||||
)
|
||||
_uiState.update(
|
||||
SendFeeLoadedTransformer(
|
||||
fees = it,
|
||||
clickIntents = this@SendFeeModel,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
isFeeApproximate = isFeeApproximate(it.normal.amount.type),
|
||||
),
|
||||
)
|
||||
updateFeeNotifications()
|
||||
},
|
||||
ifLeft = { feeError ->
|
||||
feeCheckReloadTrigger.callbackCheckResult(false)
|
||||
_uiState.update(SendFeeFailedTransformer(feeError))
|
||||
sendFeeAlertFactory.getFeeUnreachableErrorState(::feeReload)
|
||||
updateFeeNotifications()
|
||||
},
|
||||
)
|
||||
}.saveIn(feeJobHolder)
|
||||
}
|
||||
|
||||
private fun isFeeApproximate(amountType: AmountType): Boolean {
|
||||
val networkId = feeCryptoCurrencyStatus.currency.network.id
|
||||
return isFeeApproximateUseCase(
|
||||
networkId = networkId,
|
||||
amountType = amountType,
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateFeeNotifications() {
|
||||
_uiState.update(
|
||||
SendFeeNotificationsTransformer(
|
||||
cryptoCurrencyName = cryptoCurrencyStatus.currency.name,
|
||||
onFeeReload = ::feeReload,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun configFeeNavigation() {
|
||||
val params = params as? SendFeeComponentParams.FeeParams ?: return
|
||||
combine(
|
||||
flow = uiState,
|
||||
flow2 = params.currentRoute,
|
||||
transform = { state, route -> state to route },
|
||||
).onEach { (state, _) ->
|
||||
params.callback.onNavigationResult(
|
||||
NavigationUM.Content(
|
||||
title = resourceReference(R.string.common_fee_selector_title),
|
||||
subtitle = null,
|
||||
backIconRes = R.drawable.ic_back_24,
|
||||
backIconClick = params.callback::onNextClick,
|
||||
primaryButton = NavigationButton(
|
||||
textReference = resourceReference(R.string.common_continue),
|
||||
isEnabled = state.isPrimaryButtonEnabled,
|
||||
onClick = ::onNextClick,
|
||||
),
|
||||
prevButton = null,
|
||||
secondaryPairButtonsUM = null,
|
||||
),
|
||||
)
|
||||
}.launchIn(modelScope)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.converters
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal class FeeConverter(
|
||||
clickIntents: SendFeeClickIntents,
|
||||
appCurrency: AppCurrency,
|
||||
feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val fees: TransactionFee,
|
||||
) : Converter<FeeConverter.Data, Fee> {
|
||||
|
||||
private val customFeeConverter = SendFeeCustomFieldConverter(
|
||||
clickIntents = clickIntents,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
normalFee = fees.normal,
|
||||
)
|
||||
|
||||
override fun convert(value: Data): Fee {
|
||||
return when (fees) {
|
||||
is TransactionFee.Choosable -> {
|
||||
when (value.feeType) {
|
||||
FeeType.Slow -> fees.minimum
|
||||
FeeType.Market -> fees.normal
|
||||
FeeType.Fast -> fees.priority
|
||||
FeeType.Custom -> customFeeConverter.convertBack(value.customValues)
|
||||
}
|
||||
}
|
||||
is TransactionFee.Single ->
|
||||
when (value.feeType) {
|
||||
FeeType.Market -> fees.normal
|
||||
FeeType.Custom -> customFeeConverter.convertBack(value.customValues)
|
||||
else -> fees.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Data(
|
||||
val customValues: ImmutableList<CustomFeeFieldUM>,
|
||||
val feeType: FeeType,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.converters
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.bitcoin.BitcoinCustomFeeConverter
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.kaspa.KaspaCustomFeeConverter
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class SendFeeCustomFieldConverter(
|
||||
private val clickIntents: SendFeeClickIntents,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val normalFee: Fee,
|
||||
) : TwoWayConverter<Fee, ImmutableList<CustomFeeFieldUM>> {
|
||||
|
||||
private val ethereumCustomFeeConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
EthereumCustomFeeConverter(
|
||||
onCustomFeeValueChange = clickIntents::onCustomFeeValueChange,
|
||||
onNextClick = clickIntents::onNextClick,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
)
|
||||
}
|
||||
|
||||
private val bitcoinCustomFeeConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
BitcoinCustomFeeConverter(
|
||||
onCustomFeeValueChange = clickIntents::onCustomFeeValueChange,
|
||||
onNextClick = clickIntents::onNextClick,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
)
|
||||
}
|
||||
|
||||
private val kaspaCustomFeeConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
KaspaCustomFeeConverter(
|
||||
onCustomFeeValueChange = clickIntents::onCustomFeeValueChange,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
)
|
||||
}
|
||||
|
||||
override fun convert(value: Fee): ImmutableList<CustomFeeFieldUM> {
|
||||
return when (value) {
|
||||
is Fee.Ethereum -> ethereumCustomFeeConverter.convert(value)
|
||||
is Fee.Bitcoin -> bitcoinCustomFeeConverter.convert(value)
|
||||
is Fee.Kaspa -> kaspaCustomFeeConverter.convert(value)
|
||||
else -> persistentListOf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertBack(value: ImmutableList<CustomFeeFieldUM>): Fee {
|
||||
return if (value.isEmpty()) {
|
||||
normalFee
|
||||
} else {
|
||||
when (normalFee) {
|
||||
is Fee.Ethereum -> ethereumCustomFeeConverter.convertBack(normalFee = normalFee, value = value)
|
||||
is Fee.Bitcoin -> bitcoinCustomFeeConverter.convertBack(normalFee = normalFee, value = value)
|
||||
is Fee.Kaspa -> kaspaCustomFeeConverter.convertBack(normalFee = normalFee, value = value)
|
||||
else -> {
|
||||
val customFee = value.firstOrNull()
|
||||
Fee.Common(
|
||||
normalFee.amount.copy(
|
||||
value = customFee?.value?.parseToBigDecimal(customFee.decimals),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onValueChange(feeSelectorState: FeeSelectorUM.Content, index: Int, value: String) =
|
||||
when (val fee = feeSelectorState.fees.normal) {
|
||||
is Fee.Ethereum -> ethereumCustomFeeConverter.onValueChange(
|
||||
feeValue = fee,
|
||||
customValues = feeSelectorState.customValues,
|
||||
index = index,
|
||||
value = value,
|
||||
)
|
||||
is Fee.Bitcoin -> bitcoinCustomFeeConverter.onValueChange(
|
||||
customValues = feeSelectorState.customValues,
|
||||
index = index,
|
||||
value = value,
|
||||
txSize = fee.txSize,
|
||||
)
|
||||
is Fee.Kaspa -> kaspaCustomFeeConverter.onValueChange(
|
||||
customValues = feeSelectorState.customValues,
|
||||
index = index,
|
||||
value = value,
|
||||
)
|
||||
else -> feeSelectorState.customValues
|
||||
}
|
||||
|
||||
fun tryAutoFixValue(feeSelectorState: FeeSelectorUM.Content) = when (feeSelectorState.fees) {
|
||||
is TransactionFee.Choosable -> feeSelectorState.fees.minimum
|
||||
is TransactionFee.Single -> feeSelectorState.fees.normal
|
||||
}.let {
|
||||
when (it) {
|
||||
is Fee.Kaspa -> kaspaCustomFeeConverter.tryAutoFixValue(
|
||||
minimumFee = it,
|
||||
customValues = feeSelectorState.customValues,
|
||||
)
|
||||
else -> feeSelectorState.customValues
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,8 +12,8 @@ import com.tangem.core.ui.utils.parseToBigDecimal
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkExceedBalance
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.CustomFeeConverter
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import com.tangem.core.ui.utils.parseBigDecimal
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkExceedBalance
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import com.tangem.core.ui.utils.parseToBigDecimal
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkExceedBalance
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.ETHEREUM_GAS_UNIT
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.FEE_AMOUNT
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.GAS_DECIMALS
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import com.tangem.core.ui.utils.parseToBigDecimal
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkExceedBalance
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.ETHEREUM_GAS_UNIT
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.FEE_AMOUNT
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.GAS_DECIMALS
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.transformers
|
||||
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.SendFeeCustomFieldConverter
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SendFeeCustomAutoFixTransformer(
|
||||
private val clickIntents: SendFeeClickIntents,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) : Transformer<FeeUM> {
|
||||
|
||||
override fun transform(prevState: FeeUM): FeeUM {
|
||||
val state = prevState as? FeeUM.Content ?: return prevState
|
||||
val feeSelectorUM = state.feeSelectorUM as? FeeSelectorUM.Content ?: return state
|
||||
|
||||
val customFeeConverter = SendFeeCustomFieldConverter(
|
||||
clickIntents = clickIntents,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
normalFee = feeSelectorUM.fees.normal,
|
||||
)
|
||||
|
||||
return when (feeSelectorUM.selectedType) {
|
||||
FeeType.Slow,
|
||||
FeeType.Market,
|
||||
FeeType.Fast,
|
||||
-> state
|
||||
FeeType.Custom -> {
|
||||
val updatedCustomValues = customFeeConverter.tryAutoFixValue(feeSelectorUM)
|
||||
state.copy(
|
||||
feeSelectorUM = feeSelectorUM.copy(
|
||||
customValues = updatedCustomValues,
|
||||
selectedFee = customFeeConverter.convertBack(updatedCustomValues),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.transformers
|
||||
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.SendFeeCustomFieldConverter
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SendFeeCustomValueChangeTransformer(
|
||||
private val index: Int,
|
||||
private val value: String,
|
||||
private val clickIntents: SendFeeClickIntents,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) : Transformer<FeeUM> {
|
||||
|
||||
override fun transform(prevState: FeeUM): FeeUM {
|
||||
val state = prevState as? FeeUM.Content ?: return prevState
|
||||
val feeSelectorUM = state.feeSelectorUM as? FeeSelectorUM.Content ?: return state
|
||||
|
||||
val customFeeConverter = SendFeeCustomFieldConverter(
|
||||
clickIntents = clickIntents,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
normalFee = feeSelectorUM.fees.normal,
|
||||
)
|
||||
|
||||
val updatedCustomValues = customFeeConverter.onValueChange(feeSelectorUM, index, value)
|
||||
|
||||
return state.copy(
|
||||
feeSelectorUM = feeSelectorUM.copy(
|
||||
customValues = updatedCustomValues,
|
||||
selectedFee = customFeeConverter.convertBack(updatedCustomValues),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.transformers
|
||||
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SendFeeFailedTransformer(
|
||||
private val error: GetFeeError,
|
||||
) : Transformer<FeeUM> {
|
||||
|
||||
override fun transform(prevState: FeeUM): FeeUM {
|
||||
val state = prevState as? FeeUM.Content ?: return prevState
|
||||
|
||||
return state.copy(
|
||||
feeSelectorUM = FeeSelectorUM.Error(error),
|
||||
isPrimaryButtonEnabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.transformers
|
||||
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isTron
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class SendFeeInitialStateTransformer(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val appCurrency: AppCurrency,
|
||||
) : Transformer<FeeUM> {
|
||||
|
||||
private val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
|
||||
override fun transform(prevState: FeeUM): FeeUM {
|
||||
return FeeUM.Content(
|
||||
isPrimaryButtonEnabled = false,
|
||||
feeSelectorUM = FeeSelectorUM.Loading,
|
||||
notifications = persistentListOf(),
|
||||
rate = feeCryptoCurrencyStatus.value.fiatRate,
|
||||
appCurrency = appCurrency,
|
||||
isFeeApproximate = false,
|
||||
isCustomSelected = false,
|
||||
isFeeConvertibleToFiat = feeCryptoCurrencyStatus.currency.network.hasFiatFeeRate,
|
||||
isTronToken = cryptoCurrency is CryptoCurrency.Token &&
|
||||
isTron(cryptoCurrency.network.rawId),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.transformers
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.FeeConverter
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.SendFeeCustomFieldConverter
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SendFeeLoadedTransformer(
|
||||
clickIntents: SendFeeClickIntents,
|
||||
appCurrency: AppCurrency,
|
||||
feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val fees: TransactionFee,
|
||||
private val isFeeApproximate: Boolean,
|
||||
) : Transformer<FeeUM> {
|
||||
|
||||
private val customFeeFieldConverter = SendFeeCustomFieldConverter(
|
||||
clickIntents = clickIntents,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
normalFee = fees.normal,
|
||||
)
|
||||
|
||||
private val feeConverter = FeeConverter(
|
||||
clickIntents = clickIntents,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
fees = fees,
|
||||
)
|
||||
|
||||
override fun transform(prevState: FeeUM): FeeUM {
|
||||
val state = prevState as? FeeUM.Content ?: return prevState
|
||||
val feeSelectorUM = state.feeSelectorUM as? FeeSelectorUM.Content
|
||||
|
||||
val updatedFeeSelector = if (feeSelectorUM == null) {
|
||||
FeeSelectorUM.Content(
|
||||
fees = fees,
|
||||
customValues = customFeeFieldConverter.convert(fees.normal),
|
||||
selectedFee = fees.normal,
|
||||
nonce = prevState.nonce,
|
||||
)
|
||||
} else {
|
||||
FeeSelectorUM.Content(
|
||||
fees = fees,
|
||||
customValues = feeSelectorUM.customValues,
|
||||
selectedType = feeSelectorUM.selectedType,
|
||||
selectedFee = feeConverter.convert(
|
||||
FeeConverter.Data(
|
||||
feeType = feeSelectorUM.selectedType,
|
||||
customValues = feeSelectorUM.customValues,
|
||||
),
|
||||
),
|
||||
nonce = prevState.nonce,
|
||||
)
|
||||
}
|
||||
|
||||
return state.copy(
|
||||
feeSelectorUM = updatedFeeSelector,
|
||||
isFeeApproximate = isFeeApproximate,
|
||||
displayNonceInput = fees.normal is Fee.Ethereum,
|
||||
nonce = prevState.nonce,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.transformers
|
||||
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal object SendFeeLoadingTransformer : Transformer<FeeUM> {
|
||||
override fun transform(prevState: FeeUM): FeeUM {
|
||||
val state = prevState as? FeeUM.Content ?: return prevState
|
||||
return state.copy(
|
||||
feeSelectorUM = state.feeSelectorUM as? FeeSelectorUM.Content ?: FeeSelectorUM.Loading,
|
||||
notifications = persistentListOf(),
|
||||
isPrimaryButtonEnabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.transformers
|
||||
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import java.math.BigInteger
|
||||
|
||||
internal class SendFeeNonceChangeTransformer(
|
||||
private val value: String,
|
||||
) : Transformer<FeeUM> {
|
||||
|
||||
override fun transform(prevState: FeeUM): FeeUM {
|
||||
val state = prevState as? FeeUM.Content ?: return prevState
|
||||
|
||||
if (value.isEmpty()) {
|
||||
return state.copy(nonce = null)
|
||||
}
|
||||
|
||||
return try {
|
||||
val nonce = BigInteger(value)
|
||||
state.copy(
|
||||
nonce = nonce,
|
||||
feeSelectorUM = when (val feeSelectorUM = state.feeSelectorUM) {
|
||||
is FeeSelectorUM.Loading,
|
||||
is FeeSelectorUM.Error,
|
||||
-> feeSelectorUM
|
||||
is FeeSelectorUM.Content -> feeSelectorUM.copy(
|
||||
nonce = nonce,
|
||||
)
|
||||
},
|
||||
)
|
||||
} catch (e: NumberFormatException) {
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.transformers
|
||||
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addFeeUnreachableNotification
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
internal class SendFeeNotificationsTransformer(
|
||||
private val cryptoCurrencyName: String,
|
||||
private val onFeeReload: () -> Unit,
|
||||
) : Transformer<FeeUM> {
|
||||
|
||||
override fun transform(prevState: FeeUM): FeeUM {
|
||||
val state = prevState as? FeeUM.Content ?: return prevState
|
||||
|
||||
val notifications = state.getNotifications()
|
||||
return state.copy(
|
||||
notifications = notifications,
|
||||
isPrimaryButtonEnabled = state.isPrimaryButtonEnabled(notifications),
|
||||
)
|
||||
}
|
||||
|
||||
private fun FeeUM.Content.getNotifications() = buildList {
|
||||
addFeeUnreachableNotification(
|
||||
feeError = (feeSelectorUM as? FeeSelectorUM.Error)?.error,
|
||||
tokenName = cryptoCurrencyName,
|
||||
onReload = onFeeReload,
|
||||
)
|
||||
}.toImmutableList()
|
||||
|
||||
private fun FeeUM.Content.isPrimaryButtonEnabled(notifications: ImmutableList<NotificationUM>): Boolean {
|
||||
val feeSelectorState = feeSelectorUM as? FeeSelectorUM.Content ?: return false
|
||||
val customValue = feeSelectorState.customValues.firstOrNull()
|
||||
|
||||
val isNotCustom = feeSelectorState.selectedType != FeeType.Custom
|
||||
val isNotEmptyCustom = if (customValue != null) {
|
||||
!customValue.value.parseToBigDecimal(customValue.decimals).isZero() && !isNotCustom
|
||||
} else {
|
||||
false
|
||||
}
|
||||
val noErrors = notifications.none { it is NotificationUM.Error }
|
||||
|
||||
return noErrors && (isNotEmptyCustom || isNotCustom)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.transformers
|
||||
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.FeeConverter
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SendFeeSelectTransformer(
|
||||
private val feeType: FeeType,
|
||||
private val clickIntents: SendFeeClickIntents,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) : Transformer<FeeUM> {
|
||||
|
||||
override fun transform(prevState: FeeUM): FeeUM {
|
||||
val state = prevState as? FeeUM.Content ?: return prevState
|
||||
val feeSelectorUM = state.feeSelectorUM as? FeeSelectorUM.Content ?: return state
|
||||
|
||||
val feeConverter = FeeConverter(
|
||||
clickIntents = clickIntents,
|
||||
appCurrency = appCurrency,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
fees = feeSelectorUM.fees,
|
||||
)
|
||||
|
||||
val updatedFeeSelectorState = feeSelectorUM.copy(
|
||||
selectedType = feeType,
|
||||
selectedFee = feeConverter.convert(
|
||||
FeeConverter.Data(
|
||||
customValues = feeSelectorUM.customValues,
|
||||
feeType = feeType,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val isCustomFeeWasSelected = state.isCustomSelected || updatedFeeSelectorState.selectedType == FeeType.Custom
|
||||
return state.copy(
|
||||
isCustomSelected = isCustomFeeWasSelected,
|
||||
feeSelectorUM = updatedFeeSelectorState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.ui.amountScreen.utils.getFiatReference
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.rows.SelectorRowItem
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants.EMPTY_BALANCE_SIGN
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fee
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
|
||||
@Composable
|
||||
internal fun FeeBlock(feeUM: FeeUM, isClickEnabled: Boolean, onClick: () -> Unit) {
|
||||
val feeUM = feeUM as? FeeUM.Content ?: return
|
||||
val feeSelectorUM = feeUM.feeSelectorUM as? FeeSelectorUM.Content
|
||||
val isEditingDisabled = feeSelectorUM?.fees is TransactionFee.Single
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable(enabled = isClickEnabled && !isEditingDisabled, onClick = onClick)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.common_network_fee_title),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
) {
|
||||
val feeSelectorUM = feeUM.feeSelectorUM
|
||||
val feeAmount = (feeSelectorUM as? FeeSelectorUM.Content)?.selectedFee?.amount
|
||||
val (title, icon) = if (feeSelectorUM is FeeSelectorUM.Content) {
|
||||
when (feeSelectorUM.selectedType) {
|
||||
FeeType.Slow -> R.string.common_fee_selector_option_slow to R.drawable.ic_tortoise_24
|
||||
FeeType.Market -> R.string.common_fee_selector_option_market to R.drawable.ic_bird_24
|
||||
FeeType.Fast -> R.string.common_fee_selector_option_fast to R.drawable.ic_hare_24
|
||||
FeeType.Custom -> R.string.common_custom to R.drawable.ic_edit_24
|
||||
}
|
||||
} else {
|
||||
R.string.common_fee_selector_option_market to R.drawable.ic_bird_24
|
||||
}
|
||||
SelectorRowItem(
|
||||
title = resourceReference(title),
|
||||
iconRes = icon,
|
||||
preDot = remember {
|
||||
stringReference(
|
||||
feeAmount?.value.format {
|
||||
crypto(
|
||||
symbol = feeAmount?.currencySymbol.orEmpty(),
|
||||
decimals = feeAmount?.decimals ?: 0,
|
||||
).fee(canBeLower = feeUM.isFeeApproximate)
|
||||
},
|
||||
)
|
||||
},
|
||||
postDot = remember {
|
||||
if (feeUM.isFeeConvertibleToFiat) {
|
||||
getFiatReference(feeAmount?.value, feeUM.rate, feeUM.appCurrency)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
ellipsizeOffset = feeAmount?.currencySymbol?.length,
|
||||
isSelected = true,
|
||||
showDivider = false,
|
||||
showSelectedAppearance = false,
|
||||
paddingValues = PaddingValues(),
|
||||
)
|
||||
FeeLoading(feeSelectorUM)
|
||||
FeeError(feeSelectorUM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BoxScope.FeeLoading(feeSelectorUM: FeeSelectorUM) {
|
||||
AnimatedContent(
|
||||
targetState = feeSelectorUM,
|
||||
label = "Fee Loading State Change",
|
||||
modifier = Modifier.align(Alignment.CenterEnd),
|
||||
) {
|
||||
if (it == FeeSelectorUM.Loading) {
|
||||
RectangleShimmer(
|
||||
radius = 3.dp,
|
||||
modifier = Modifier.size(
|
||||
height = 12.dp,
|
||||
width = 90.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BoxScope.FeeError(feeSelectorUM: FeeSelectorUM) {
|
||||
AnimatedContent(
|
||||
targetState = feeSelectorUM,
|
||||
label = "Fee Error State Change",
|
||||
modifier = Modifier.align(Alignment.CenterEnd),
|
||||
) {
|
||||
if (it is FeeSelectorUM.Error) {
|
||||
Text(
|
||||
text = EMPTY_BALANCE_SIGN,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.ui
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.containers.FooterContainer
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnter
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnterAmount
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnterInfoAmount
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun SendCustomFee(
|
||||
customValues: ImmutableList<CustomFeeFieldUM>,
|
||||
selectedFee: FeeType,
|
||||
hasNotifications: Boolean,
|
||||
onValueChange: (Int, String) -> Unit,
|
||||
onNonceChange: (String) -> Unit,
|
||||
displayNonceInput: Boolean,
|
||||
nonce: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = selectedFee == FeeType.Custom && customValues.isNotEmpty(),
|
||||
label = "Custom Fee Selected Animation",
|
||||
enter = expandVertically().plus(fadeIn()),
|
||||
exit = shrinkVertically().plus(fadeOut()),
|
||||
) {
|
||||
val bottomPadding = if (hasNotifications) 12.dp else 0.dp
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = modifier.padding(bottom = bottomPadding),
|
||||
) {
|
||||
repeat(customValues.size) { index ->
|
||||
val value = customValues[index]
|
||||
FooterContainer(
|
||||
footer = value.footer,
|
||||
) {
|
||||
if (value.label != null) {
|
||||
InputRowEnterInfoAmount(
|
||||
text = value.value,
|
||||
decimals = value.decimals,
|
||||
symbol = value.symbol,
|
||||
title = value.title,
|
||||
info = value.label,
|
||||
keyboardOptions = value.keyboardOptions,
|
||||
keyboardActions = value.keyboardActions,
|
||||
onValueChange = { onValueChange(index, it) },
|
||||
showDivider = false,
|
||||
isReadOnly = value.isReadonly,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
InputRowEnterAmount(
|
||||
text = value.value,
|
||||
decimals = value.decimals,
|
||||
title = value.title,
|
||||
symbol = value.symbol,
|
||||
onValueChange = { onValueChange(index, it) },
|
||||
keyboardOptions = value.keyboardOptions,
|
||||
keyboardActions = value.keyboardActions,
|
||||
showDivider = false,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (displayNonceInput) {
|
||||
Nonce(
|
||||
onNonceChange = onNonceChange,
|
||||
nonce = nonce,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Nonce(onNonceChange: (String) -> Unit, nonce: String?) {
|
||||
FooterContainer(
|
||||
footer = resourceReference(R.string.send_nonce_footer),
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
) {
|
||||
InputRowEnter(
|
||||
text = nonce.orEmpty(),
|
||||
title = resourceReference(R.string.send_nonce),
|
||||
onValueChange = onNonceChange,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
placeholder = resourceReference(R.string.send_nonce_hint),
|
||||
showDivider = false,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.notifications
|
||||
|
||||
private const val FEE_SELECTOR_KEY = "FEE_SELECTOR_KEY"
|
||||
private const val FEE_CUSTOM_KEY = "FEE_CUSTOM_KEY"
|
||||
|
||||
@Composable
|
||||
internal fun SendFeeContent(state: FeeUM, clickIntents: SendFeeClickIntents) {
|
||||
if (state !is FeeUM.Content) return
|
||||
|
||||
val notifications = state.notifications
|
||||
val feeSelectorUM = state.feeSelectorUM as? FeeSelectorUM.Content
|
||||
val isCustomSelected = feeSelectorUM?.selectedType == FeeType.Custom
|
||||
val hasNotifications = notifications.isNotEmpty()
|
||||
LazyColumn(
|
||||
modifier = Modifier // Do not put fillMaxSize() in here
|
||||
.background(TangemTheme.colors.background.tertiary)
|
||||
.padding(
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp,
|
||||
),
|
||||
) {
|
||||
feeSelector(state, clickIntents)
|
||||
if (feeSelectorUM != null) {
|
||||
customFee(
|
||||
feeSelectorUM = feeSelectorUM,
|
||||
onValueChange = clickIntents::onCustomFeeValueChange,
|
||||
onNonceChange = clickIntents::onNonceChange,
|
||||
displayNonceInput = state.displayNonceInput,
|
||||
nonce = state.nonce?.toString().orEmpty(),
|
||||
hasNotifications = hasNotifications,
|
||||
)
|
||||
}
|
||||
notifications(notifications = notifications, hasPaddingAbove = isCustomSelected)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.feeSelector(state: FeeUM.Content, clickIntents: SendFeeClickIntents) {
|
||||
item(key = FEE_SELECTOR_KEY) {
|
||||
SendSpeedSelector(
|
||||
state = state,
|
||||
clickIntents = clickIntents,
|
||||
modifier = Modifier.animateItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal fun LazyListScope.customFee(
|
||||
feeSelectorUM: FeeSelectorUM.Content,
|
||||
hasNotifications: Boolean,
|
||||
onValueChange: (Int, String) -> Unit,
|
||||
onNonceChange: (String) -> Unit,
|
||||
displayNonceInput: Boolean,
|
||||
nonce: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
item(key = FEE_CUSTOM_KEY) {
|
||||
SendCustomFee(
|
||||
customValues = feeSelectorUM.customValues,
|
||||
selectedFee = feeSelectorUM.selectedType,
|
||||
hasNotifications = hasNotifications,
|
||||
onValueChange = onValueChange,
|
||||
onNonceChange = onNonceChange,
|
||||
displayNonceInput = displayNonceInput,
|
||||
nonce = nonce,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.animateItem()
|
||||
.background(TangemTheme.colors.background.tertiary)
|
||||
.padding(top = 12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
internal fun SendSpeedSelector(
|
||||
state: FeeUM.Content,
|
||||
clickIntents: SendFeeClickIntents,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
) {
|
||||
SendSpeedSelectorItem(
|
||||
titleRes = R.string.common_fee_selector_option_slow,
|
||||
iconRes = R.drawable.ic_tortoise_24,
|
||||
feeType = FeeType.Slow,
|
||||
state = state,
|
||||
onSelect = { clickIntents.onFeeSelectorClick(FeeType.Slow) },
|
||||
)
|
||||
SendSpeedSelectorItem(
|
||||
titleRes = R.string.common_fee_selector_option_market,
|
||||
iconRes = R.drawable.ic_bird_24,
|
||||
feeType = FeeType.Market,
|
||||
state = state,
|
||||
onSelect = { clickIntents.onFeeSelectorClick(FeeType.Market) },
|
||||
)
|
||||
SendSpeedSelectorItem(
|
||||
titleRes = R.string.common_fee_selector_option_fast,
|
||||
iconRes = R.drawable.ic_hare_24,
|
||||
feeType = FeeType.Fast,
|
||||
state = state,
|
||||
onSelect = { clickIntents.onFeeSelectorClick(FeeType.Fast) },
|
||||
)
|
||||
SendSpeedSelectorItem(
|
||||
titleRes = R.string.common_custom,
|
||||
iconRes = R.drawable.ic_edit_24,
|
||||
feeType = FeeType.Custom,
|
||||
state = state,
|
||||
onSelect = { clickIntents.onFeeSelectorClick(FeeType.Custom) },
|
||||
)
|
||||
}
|
||||
FooterText(clickIntents::onReadMoreClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FooterText(onReadMoreClick: () -> Unit) {
|
||||
val linkText = stringResourceSafe(R.string.common_read_more)
|
||||
val fullString = stringResourceSafe(R.string.common_fee_selector_footer, linkText)
|
||||
val linkTextPosition = fullString.length - linkText.length
|
||||
val defaultStyle = TangemTheme.colors.text.tertiary
|
||||
val linkStyle = TangemTheme.colors.text.accent
|
||||
val annotatedString = remember(defaultStyle, linkStyle) {
|
||||
buildAnnotatedString {
|
||||
withStyle(SpanStyle(defaultStyle)) {
|
||||
append(fullString.substring(0, linkTextPosition))
|
||||
}
|
||||
withStyle(SpanStyle(linkStyle)) {
|
||||
append(fullString.substring(linkTextPosition, fullString.length))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val click = { i: Int ->
|
||||
val readMoreStyle = requireNotNull(annotatedString.spanStyles.getOrNull(1))
|
||||
if (i in readMoreStyle.start..readMoreStyle.end) {
|
||||
onReadMoreClick()
|
||||
}
|
||||
}
|
||||
|
||||
ClickableText(
|
||||
text = annotatedString,
|
||||
style = TangemTheme.typography.caption2.copy(textAlign = TextAlign.Start),
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
onClick = click,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.ui
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.ui.amountScreen.utils.getFiatReference
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.rows.SelectorRowItem
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants.EMPTY_BALANCE_SIGN
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fee
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
|
||||
@Composable
|
||||
internal fun SendSpeedSelectorItem(
|
||||
@StringRes titleRes: Int,
|
||||
@DrawableRes iconRes: Int,
|
||||
feeType: FeeType,
|
||||
state: FeeUM.Content,
|
||||
onSelect: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val feeSelectorState = state.feeSelectorUM
|
||||
val content = feeSelectorState as? FeeSelectorUM.Content
|
||||
val amount = content?.getAmount(feeType)
|
||||
val (showDivider, isVisible) = content.getDividerAndVisibility(feeType)
|
||||
AnimatedVisibility(
|
||||
visible = isVisible,
|
||||
label = "Fee Selector Visibility Animation",
|
||||
enter = expandVertically().plus(fadeIn()),
|
||||
exit = shrinkVertically().plus(fadeOut()),
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onSelect() },
|
||||
) {
|
||||
SelectorRowItem(
|
||||
title = resourceReference(titleRes),
|
||||
iconRes = iconRes,
|
||||
onSelect = onSelect,
|
||||
modifier = modifier,
|
||||
preDot = stringReference(
|
||||
amount?.value.format {
|
||||
crypto(
|
||||
symbol = amount?.currencySymbol.orEmpty(),
|
||||
decimals = amount?.decimals ?: 0,
|
||||
).fee(canBeLower = state.isFeeApproximate)
|
||||
},
|
||||
),
|
||||
postDot = if (state.isFeeConvertibleToFiat) {
|
||||
getFiatReference(amount?.value, state.rate, state.appCurrency)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
ellipsizeOffset = amount?.currencySymbol?.length,
|
||||
isSelected = content?.selectedType == feeType,
|
||||
showDivider = showDivider,
|
||||
)
|
||||
FeeLoading(feeSelectorState)
|
||||
FeeError(feeSelectorState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeeLoading(feeSelectorState: FeeSelectorUM) {
|
||||
Row {
|
||||
SpacerWMax()
|
||||
AnimatedVisibility(
|
||||
visible = feeSelectorState == FeeSelectorUM.Loading,
|
||||
label = "Fee Loading State Change",
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
) {
|
||||
RectangleShimmer(
|
||||
radius = 3.dp,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
vertical = 18.dp,
|
||||
horizontal = 12.dp,
|
||||
)
|
||||
.size(
|
||||
height = 12.dp,
|
||||
width = 90.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeeError(feeSelectorState: FeeSelectorUM) {
|
||||
Row {
|
||||
SpacerWMax()
|
||||
AnimatedVisibility(
|
||||
visible = feeSelectorState is FeeSelectorUM.Error,
|
||||
label = "Fee Error State Change",
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
) {
|
||||
Text(
|
||||
text = EMPTY_BALANCE_SIGN,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.body2,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
vertical = 14.dp,
|
||||
horizontal = 12.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun FeeSelectorUM.Content.getAmount(feeType: FeeType): Amount? {
|
||||
val choosableFees = fees as? TransactionFee.Choosable
|
||||
val decimals = fees.normal.amount.decimals
|
||||
val customValue = this.customValues.firstOrNull()?.value?.parseToBigDecimal(decimals)
|
||||
val customAmount = fees.normal.amount.copy(value = customValue)
|
||||
return when (feeType) {
|
||||
FeeType.Slow -> choosableFees?.minimum?.amount
|
||||
FeeType.Market -> fees.normal.amount
|
||||
FeeType.Fast -> choosableFees?.priority?.amount
|
||||
FeeType.Custom -> customAmount
|
||||
}
|
||||
}
|
||||
|
||||
private fun FeeSelectorUM.Content?.getDividerAndVisibility(feeType: FeeType): Pair<Boolean, Boolean> {
|
||||
val hasCustomValues = !this?.customValues.isNullOrEmpty()
|
||||
val isNotSingle = this?.fees !is TransactionFee.Single
|
||||
return when (feeType) {
|
||||
FeeType.Slow -> true to isNotSingle
|
||||
FeeType.Market -> (isNotSingle || hasCustomValues) to true
|
||||
FeeType.Fast -> hasCustomValues to isNotSingle
|
||||
FeeType.Custom -> false to hasCustomValues
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.ui.state
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigInteger
|
||||
|
||||
@Stable
|
||||
internal sealed class FeeSelectorUM {
|
||||
|
||||
data class Content(
|
||||
val fees: TransactionFee,
|
||||
val selectedType: FeeType = FeeType.Market,
|
||||
val selectedFee: Fee?,
|
||||
val customValues: ImmutableList<CustomFeeFieldUM> = persistentListOf(),
|
||||
val nonce: BigInteger? = null,
|
||||
) : FeeSelectorUM()
|
||||
|
||||
data object Loading : FeeSelectorUM()
|
||||
|
||||
data class Error(
|
||||
val error: GetFeeError,
|
||||
) : FeeSelectorUM()
|
||||
}
|
||||
|
||||
internal enum class FeeType {
|
||||
Slow,
|
||||
Market,
|
||||
Fast,
|
||||
Custom,
|
||||
;
|
||||
|
||||
fun toAnalyticType(feeSelectorUM: FeeSelectorUM.Content): AnalyticsParam.FeeType = when (feeSelectorUM.fees) {
|
||||
is TransactionFee.Single -> AnalyticsParam.FeeType.Fixed
|
||||
is TransactionFee.Choosable -> when (feeSelectorUM.selectedType) {
|
||||
Slow -> AnalyticsParam.FeeType.Min
|
||||
Market -> AnalyticsParam.FeeType.Normal
|
||||
Fast -> AnalyticsParam.FeeType.Max
|
||||
Custom -> AnalyticsParam.FeeType.Custom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.ui.state
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
|
||||
@Stable
|
||||
internal sealed class FeeUM {
|
||||
|
||||
abstract val isPrimaryButtonEnabled: Boolean
|
||||
|
||||
data class Content(
|
||||
override val isPrimaryButtonEnabled: Boolean,
|
||||
val feeSelectorUM: FeeSelectorUM,
|
||||
val rate: BigDecimal?,
|
||||
val appCurrency: AppCurrency,
|
||||
val isFeeConvertibleToFiat: Boolean,
|
||||
val isFeeApproximate: Boolean,
|
||||
val isCustomSelected: Boolean,
|
||||
val isTronToken: Boolean,
|
||||
val customValues: ImmutableList<CustomFeeFieldUM> = persistentListOf(),
|
||||
val displayNonceInput: Boolean = false,
|
||||
val nonce: BigInteger? = null,
|
||||
val notifications: ImmutableList<NotificationUM>,
|
||||
val isEditingDisabled: Boolean = false,
|
||||
) : FeeUM()
|
||||
|
||||
data class Empty(
|
||||
override val isPrimaryButtonEnabled: Boolean = false,
|
||||
) : FeeUM()
|
||||
}
|
||||
|
|
@ -36,10 +36,10 @@ import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase
|
|||
import com.tangem.domain.utils.convertToSdkAmount
|
||||
import com.tangem.features.send.v2.api.SendNotificationsComponent
|
||||
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkAndCalculateSubtractedAmount
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkFeeCoverage
|
||||
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateListener
|
||||
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkAndCalculateSubtractedAmount
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkFeeCoverage
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.analytics.NotificationsAnalyticEvents
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isTron
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.send.v2.api.entity.*
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendConfirmationNotificationsTransformer
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendConfirmationNotificationsTransformerV2
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendConfirmationNotificationsTransformer
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ internal class SwapAmountFieldConverter(
|
|||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus),
|
||||
iconStateConverter = iconStateConverter,
|
||||
isRedesignEnabled = true,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
).convert(
|
||||
AmountParameters(
|
||||
|
|
|
|||
|
|
@ -311,7 +311,6 @@ private fun SendWithSwapSuccessContent_Preview() {
|
|||
networkName = "Polygon",
|
||||
isValidating = false,
|
||||
isInitialized = false,
|
||||
isRedesignEnabled = false,
|
||||
isRecentHidden = false,
|
||||
),
|
||||
feeSelectorUM = FeeSelectorUM.Content(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import com.arkivanov.decompose.router.slot.activate
|
|||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRoute.*
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.bottomsheet.receive.AddressModel
|
||||
import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
|
||||
|
|
@ -81,7 +80,6 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBala
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express.ExpressStatusFactory
|
||||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
import com.tangem.features.tokendetails.TokenDetailsComponent
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
|
||||
|
|
@ -140,7 +138,6 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
private val router: InnerTokenDetailsRouter,
|
||||
private val tokenDetailsDeepLinkActionListener: TokenDetailsDeepLinkActionListener,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle,
|
||||
private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase,
|
||||
private val getEnsNameUseCase: GetEnsNameUseCase,
|
||||
|
|
@ -551,17 +548,10 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun sendCurrency() {
|
||||
val route = if (sendFeatureToggles.isSendWithSwapEnabled) {
|
||||
AppRoute.SendEntryPoint(
|
||||
currency = cryptoCurrency,
|
||||
userWalletId = userWallet.walletId,
|
||||
)
|
||||
} else {
|
||||
AppRoute.Send(
|
||||
currency = cryptoCurrency,
|
||||
userWalletId = userWallet.walletId,
|
||||
)
|
||||
}
|
||||
val route = AppRoute.SendEntryPoint(
|
||||
currency = cryptoCurrency,
|
||||
userWalletId = userWallet.walletId,
|
||||
)
|
||||
|
||||
appRouter.push(route)
|
||||
}
|
||||
|
|
@ -1181,7 +1171,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
TokenAction.Receive -> navigateToReceive()
|
||||
TokenAction.Send -> sendCurrency()
|
||||
TokenAction.Swap -> appRouter.push(
|
||||
Swap(
|
||||
AppRoute.Swap(
|
||||
currencyFrom = cryptoCurrency,
|
||||
userWalletId = userWalletId,
|
||||
screenSource = AnalyticsParam.ScreensSources.Token.value,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue