Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-07 18:10:46 +05:00
parent b01b0ef4ca
commit 410ecb6fc0
14 changed files with 311 additions and 195 deletions

View file

@ -1,6 +1,6 @@
package com.tangem.features.send.v2.api
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.params.FeeSelectorParams
@ -9,5 +9,11 @@ interface FeeSelectorBlockComponent : ComposableContentComponent {
fun updateState(feeSelectorUM: FeeSelectorUM)
interface Factory : ComponentFactory<FeeSelectorParams.FeeSelectorBlockParams, FeeSelectorBlockComponent>
interface Factory {
fun create(
context: AppComponentContext,
params: FeeSelectorParams.FeeSelectorBlockParams,
onResult: (feeSelectorUM: FeeSelectorUM) -> Unit,
): FeeSelectorBlockComponent
}
}

View file

@ -1,9 +1,15 @@
package com.tangem.features.send.v2.api
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.send.v2.api.params.FeeSelectorParams
interface FeeSelectorComponent : ComposableBottomSheetComponent {
interface Factory : ComponentFactory<FeeSelectorParams.FeeSelectorDetailsParams, FeeSelectorComponent>
interface Factory {
fun create(
context: AppComponentContext,
params: FeeSelectorParams.FeeSelectorDetailsParams,
onDismiss: () -> Unit,
): FeeSelectorComponent
}
}

View file

@ -13,27 +13,33 @@ sealed class FeeSelectorParams {
abstract val state: FeeSelectorUM
abstract val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>
abstract val cryptoCurrencyStatus: CryptoCurrencyStatus
abstract val callback: FeeSelectorModelCallback
abstract val suggestedFeeState: SuggestedFeeState
abstract val feeDisplaySource: FeeDisplaySource
data class FeeSelectorBlockParams(
override val state: FeeSelectorUM,
override val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>,
override val cryptoCurrencyStatus: CryptoCurrencyStatus,
override val callback: FeeSelectorModelCallback,
override val suggestedFeeState: SuggestedFeeState,
override val feeDisplaySource: FeeDisplaySource,
) : FeeSelectorParams()
data class FeeSelectorDetailsParams(
override val state: FeeSelectorUM,
override val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>,
override val cryptoCurrencyStatus: CryptoCurrencyStatus,
override val callback: FeeSelectorModelCallback,
override val suggestedFeeState: SuggestedFeeState,
override val feeDisplaySource: FeeDisplaySource,
val callback: FeeSelectorModelCallback,
) : FeeSelectorParams()
sealed class SuggestedFeeState {
data object None : SuggestedFeeState()
data class Suggestion(val title: TextReference, val fee: Fee) : SuggestedFeeState()
}
enum class FeeDisplaySource {
Screen,
BottomSheet,
}
}

View file

@ -1,63 +1,65 @@
package com.tangem.features.send.v2.feeselector
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.foundation.clickable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.ui.amountScreen.utils.getFiatString
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.slot.activate
import com.arkivanov.decompose.router.slot.childSlot
import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.atoms.text.EllipsisText
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.core.ui.res.TangemThemePreview
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.core.ui.extensions.conditional
import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
import com.tangem.features.send.v2.api.entity.FeeFiatRateUM
import com.tangem.features.send.v2.api.entity.FeeItem
import com.tangem.features.send.v2.api.FeeSelectorComponent
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.params.FeeSelectorParams
import com.tangem.features.send.v2.feeselector.model.FeeSelectorModel
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.feeselector.ui.FeeSelectorBlockContent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import java.math.BigDecimal
import kotlinx.serialization.builtins.serializer
internal class DefaultFeeSelectorBlockComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: FeeSelectorParams.FeeSelectorBlockParams,
@Assisted private val params: FeeSelectorParams.FeeSelectorBlockParams,
@Assisted onResult: (feeSelectorUM: FeeSelectorUM) -> Unit,
private val feeSelectorComponentFactory: FeeSelectorComponent.Factory,
) : FeeSelectorBlockComponent, AppComponentContext by appComponentContext {
private val model: FeeSelectorModel = getOrCreateModel(params = params)
private val bottomSheetSlot = childSlot(
source = model.feeSelectorBottomSheet,
serializer = Unit.serializer(),
handleBackButton = false,
childFactory = { _, componentContext ->
feeSelectorComponentFactory.create(
context = childByContext(componentContext),
params = FeeSelectorParams.FeeSelectorDetailsParams(
state = model.uiState.value,
onLoadFee = params.onLoadFee,
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
callback = model,
suggestedFeeState = FeeSelectorParams.SuggestedFeeState.None,
feeDisplaySource = FeeSelectorParams.FeeDisplaySource.Screen,
),
onDismiss = {
model.feeSelectorBottomSheet.dismiss()
},
)
},
)
init {
model.uiState
.onEach(params.callback::onFeeResult)
.onEach(onResult)
.launchIn(componentScope)
}
@ -68,7 +70,18 @@ internal class DefaultFeeSelectorBlockComponent @AssistedInject constructor(
@Composable
override fun Content(modifier: Modifier) {
val state by model.uiState.collectAsStateWithLifecycle()
FeeSelectorBlockContent(modifier = modifier, state = state)
val bottomSheet by bottomSheetSlot.subscribeAsState()
FeeSelectorBlockContent(
state = state,
modifier = modifier
.conditional(params.feeDisplaySource == FeeSelectorParams.FeeDisplaySource.Screen) {
Modifier.clickable {
model.feeSelectorBottomSheet.activate(Unit)
}
},
)
bottomSheet.child?.instance?.BottomSheet()
}
@AssistedFactory
@ -76,122 +89,7 @@ internal class DefaultFeeSelectorBlockComponent @AssistedInject constructor(
override fun create(
context: AppComponentContext,
params: FeeSelectorParams.FeeSelectorBlockParams,
onResult: (feeSelectorUM: FeeSelectorUM) -> Unit,
): DefaultFeeSelectorBlockComponent
}
}
@Composable
private fun FeeSelectorBlockContent(state: FeeSelectorUM, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.background(TangemTheme.colors.background.action)
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
modifier = Modifier.size(24.dp),
painter = painterResource(R.drawable.ic_fee_new_24),
contentDescription = null,
tint = TangemTheme.colors.icon.accent,
)
Text(
modifier = Modifier.padding(start = TangemTheme.dimens.spacing4),
text = stringResourceSafe(R.string.common_network_fee_title),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
)
Icon(
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing6)
.size(TangemTheme.dimens.size16),
painter = painterResource(id = R.drawable.ic_token_info_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
)
SpacerWMax()
when (state) {
is FeeSelectorUM.Content -> FeeContent(state)
is FeeSelectorUM.Loading -> FeeLoading()
is FeeSelectorUM.Error -> FeeError()
}
}
}
@Composable
private fun RowScope.FeeError() {
Text(
text = EMPTY_BALANCE_SIGN,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.body2,
)
}
@Composable
private fun RowScope.FeeLoading() {
TextShimmer(
radius = TangemTheme.dimens.radius3,
style = TangemTheme.typography.body1,
modifier = Modifier.width(width = TangemTheme.dimens.size90),
)
}
@Composable
private fun RowScope.FeeContent(state: FeeSelectorUM.Content) {
val fiatRate = state.feeFiatRateUM
EllipsisText(
text = if (fiatRate != null) {
getFiatString(
value = state.selectedFeeItem.fee.amount.value,
rate = fiatRate.rate,
appCurrency = fiatRate.appCurrency,
approximate = state.isFeeApproximate,
)
} else {
state.selectedFeeItem.fee.amount.value.format {
crypto(
symbol = state.selectedFeeItem.fee.amount.currencySymbol,
decimals = state.selectedFeeItem.fee.amount.decimals,
).fee(canBeLower = state.isFeeApproximate)
}
},
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.End,
modifier = Modifier
.weight(1f)
.padding(start = TangemTheme.dimens.spacing4),
)
Icon(
modifier = Modifier.size(width = 18.dp, height = 24.dp),
painter = painterResource(id = R.drawable.ic_select_18_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
)
}
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO)
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun FeeSelectorBlockContent_Preview() {
TangemThemePreview {
val feeItem = FeeItem.Market(
Fee.Common(amount = Amount(value = BigDecimal("0.0002876"), blockchain = Blockchain.Ethereum)),
)
FeeSelectorBlockContent(
modifier = Modifier.fillMaxWidth(),
state = FeeSelectorUM.Content(
feeItems = persistentListOf(feeItem),
selectedFeeItem = feeItem,
isFeeApproximate = false,
feeFiatRateUM = FeeFiatRateUM(
rate = BigDecimal("2500"),
appCurrency = AppCurrency.Default,
),
displayNonceInput = false,
nonce = null,
onNonceChange = {},
fees = TransactionFee.Single(feeItem.fee),
),
)
}
}

View file

@ -16,18 +16,24 @@ import dagger.assisted.AssistedInject
internal class DefaultFeeSelectorComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted private val params: FeeSelectorParams.FeeSelectorDetailsParams,
@Assisted private val onDismiss: () -> Unit,
) : FeeSelectorComponent, AppComponentContext by appComponentContext {
private val model: FeeSelectorModel = getOrCreateModel(params = params)
override fun dismiss() {
model.dismiss()
onDismiss()
}
@Composable
override fun BottomSheet() {
val state by model.uiState.collectAsStateWithLifecycle()
FeeSelectorModalBottomSheet(onDismiss = ::dismiss, state = state, feeSelectorIntents = model)
FeeSelectorModalBottomSheet(
onDismiss = ::dismiss,
state = state,
feeSelectorIntents = model,
feeDisplaySource = params.feeDisplaySource,
)
}
@AssistedFactory
@ -35,6 +41,7 @@ internal class DefaultFeeSelectorComponent @AssistedInject constructor(
override fun create(
context: AppComponentContext,
params: FeeSelectorParams.FeeSelectorDetailsParams,
onDismiss: () -> Unit,
): DefaultFeeSelectorComponent
}
}

View file

@ -2,14 +2,16 @@ package com.tangem.features.send.v2.feeselector.model
import androidx.compose.runtime.Stable
import arrow.core.getOrElse
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.blockchain.common.AmountType
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.decompose.navigation.Router
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback
import com.tangem.features.send.v2.api.entity.FeeItem
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.params.FeeSelectorParams
@ -30,13 +32,14 @@ internal class FeeSelectorModel @Inject constructor(
paramsContainer: ParamsContainer,
private val isFeeApproximateUseCase: IsFeeApproximateUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val router: Router,
override val dispatchers: CoroutineDispatcherProvider,
) : Model(), FeeSelectorIntents {
) : Model(), FeeSelectorIntents, FeeSelectorModelCallback {
private val params = paramsContainer.require<FeeSelectorParams>()
private var appCurrency: AppCurrency = AppCurrency.Default
val feeSelectorBottomSheet = SlotNavigation<Unit>()
val uiState: StateFlow<FeeSelectorUM>
field = MutableStateFlow<FeeSelectorUM>(params.state)
@ -49,10 +52,6 @@ internal class FeeSelectorModel @Inject constructor(
uiState.value = feeSelectorUM
}
fun dismiss() {
router.pop()
}
private fun initAppCurrency() {
modelScope.launch {
appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }
@ -102,7 +101,11 @@ internal class FeeSelectorModel @Inject constructor(
}
override fun onDoneClick() {
params.callback.onFeeResult(uiState.value)
dismiss()
(params as? FeeSelectorParams.FeeSelectorDetailsParams)?.callback?.onFeeResult(uiState.value)
}
override fun onFeeResult(feeSelectorUM: FeeSelectorUM) {
uiState.value = feeSelectorUM
feeSelectorBottomSheet.dismiss()
}
}

View file

@ -0,0 +1,153 @@
package com.tangem.features.send.v2.feeselector.ui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.ui.amountScreen.utils.getFiatString
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.atoms.text.EllipsisText
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.core.ui.res.TangemThemePreview
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.features.send.v2.api.entity.FeeFiatRateUM
import com.tangem.features.send.v2.api.entity.FeeItem
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.impl.R
import kotlinx.collections.immutable.persistentListOf
import java.math.BigDecimal
@Composable
internal fun FeeSelectorBlockContent(state: FeeSelectorUM, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.background(TangemTheme.colors.background.action)
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
modifier = Modifier.size(24.dp),
painter = painterResource(R.drawable.ic_fee_new_24),
contentDescription = null,
tint = TangemTheme.colors.icon.accent,
)
Text(
modifier = Modifier.padding(start = TangemTheme.dimens.spacing4),
text = stringResourceSafe(R.string.common_network_fee_title),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
)
Icon(
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing6)
.size(TangemTheme.dimens.size16),
painter = painterResource(id = R.drawable.ic_token_info_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
)
SpacerWMax()
when (state) {
is FeeSelectorUM.Content -> FeeContent(state)
is FeeSelectorUM.Loading -> FeeLoading()
is FeeSelectorUM.Error -> FeeError()
}
}
}
@Composable
private fun RowScope.FeeError() {
Text(
text = EMPTY_BALANCE_SIGN,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.body2,
)
}
@Composable
private fun RowScope.FeeLoading() {
TextShimmer(
radius = TangemTheme.dimens.radius3,
style = TangemTheme.typography.body1,
modifier = Modifier.width(width = TangemTheme.dimens.size90),
)
}
@Composable
private fun RowScope.FeeContent(state: FeeSelectorUM.Content) {
val fiatRate = state.feeFiatRateUM
EllipsisText(
text = if (fiatRate != null) {
getFiatString(
value = state.selectedFeeItem.fee.amount.value,
rate = fiatRate.rate,
appCurrency = fiatRate.appCurrency,
approximate = state.isFeeApproximate,
)
} else {
state.selectedFeeItem.fee.amount.value.format {
crypto(
symbol = state.selectedFeeItem.fee.amount.currencySymbol,
decimals = state.selectedFeeItem.fee.amount.decimals,
).fee(canBeLower = state.isFeeApproximate)
}
},
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.End,
modifier = Modifier
.weight(1f)
.padding(start = TangemTheme.dimens.spacing4),
)
Icon(
modifier = Modifier.size(width = 18.dp, height = 24.dp),
painter = painterResource(id = R.drawable.ic_select_18_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
)
}
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO)
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun FeeSelectorBlockContent_Preview() {
TangemThemePreview {
val feeItem = FeeItem.Market(
Fee.Common(amount = Amount(value = BigDecimal("0.0002876"), blockchain = Blockchain.Ethereum)),
)
FeeSelectorBlockContent(
modifier = Modifier.fillMaxWidth(),
state = FeeSelectorUM.Content(
feeItems = persistentListOf(feeItem),
selectedFeeItem = feeItem,
isFeeApproximate = false,
feeFiatRateUM = FeeFiatRateUM(
rate = BigDecimal("2500"),
appCurrency = AppCurrency.Default,
),
displayNonceInput = false,
nonce = null,
onNonceChange = {},
fees = TransactionFee.Single(feeItem.fee),
),
)
}
}

View file

@ -4,10 +4,8 @@ import android.content.res.Configuration
import androidx.annotation.DrawableRes
import androidx.compose.animation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.HorizontalDivider
@ -17,7 +15,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.KeyboardType
@ -52,6 +49,7 @@ import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
import com.tangem.features.send.v2.api.entity.FeeFiatRateUM
import com.tangem.features.send.v2.api.entity.FeeItem
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.params.FeeSelectorParams
import com.tangem.features.send.v2.feeselector.model.FeeSelectorIntents
import com.tangem.features.send.v2.feeselector.model.StubFeeSelectorIntents
import com.tangem.features.send.v2.impl.R
@ -65,6 +63,7 @@ import java.math.BigInteger
internal fun FeeSelectorModalBottomSheet(
state: FeeSelectorUM,
feeSelectorIntents: FeeSelectorIntents,
feeDisplaySource: FeeSelectorParams.FeeDisplaySource,
onDismiss: () -> Unit,
) {
if (state !is FeeSelectorUM.Content) return
@ -77,17 +76,13 @@ internal fun FeeSelectorModalBottomSheet(
),
containerColor = TangemTheme.colors.background.primary,
title = {
TangemModalBottomSheetTitle(
title = resourceReference(R.string.common_network_fee_title),
startIconRes = R.drawable.ic_back_24,
onStartClick = onDismiss,
)
FeeTitle(feeDisplaySource = feeDisplaySource, onDismiss = onDismiss)
},
content = {
FeeSelectorItems(
state = state,
feeSelectorIntents = feeSelectorIntents,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 16.dp),
modifier = Modifier.padding(vertical = 4.dp, horizontal = 13.dp),
)
},
footer = {
@ -102,6 +97,26 @@ internal fun FeeSelectorModalBottomSheet(
)
}
@Composable
private fun FeeTitle(feeDisplaySource: FeeSelectorParams.FeeDisplaySource, onDismiss: () -> Unit) {
when (feeDisplaySource) {
FeeSelectorParams.FeeDisplaySource.Screen -> {
TangemModalBottomSheetTitle(
title = resourceReference(R.string.common_network_fee_title),
endIconRes = R.drawable.ic_close_24,
onEndClick = onDismiss,
)
}
FeeSelectorParams.FeeDisplaySource.BottomSheet -> {
TangemModalBottomSheetTitle(
title = resourceReference(R.string.common_network_fee_title),
startIconRes = R.drawable.ic_back_24,
onStartClick = onDismiss,
)
}
}
}
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
private fun FeeSelectorItems(
@ -129,21 +144,7 @@ private fun FeeSelectorItems(
val itemModifier = Modifier
.fillMaxWidth()
.background(TangemTheme.colors.background.primary)
.then(
if (isSelected) {
Modifier
.border(
width = 2.5.dp,
color = iconTint.copy(alpha = 0.2F),
shape = RoundedCornerShape(16.dp),
)
.padding(2.5.dp)
.border(width = 1.dp, color = iconTint, shape = RoundedCornerShape(14.dp))
.clip(RoundedCornerShape(14.dp))
} else {
Modifier
},
)
.selectedBorder(isSelected = isSelected)
.clickableSingle(onClick = { feeSelectorIntents.onFeeItemSelected(item) })
when (item) {
is FeeItem.Suggested -> RegularFeeItemContent(
@ -477,7 +478,12 @@ private fun FeeSelectorBS_Preview(
state: FeeSelectorUM.Content,
) {
TangemThemePreview {
FeeSelectorModalBottomSheet(onDismiss = {}, state = state, feeSelectorIntents = StubFeeSelectorIntents())
FeeSelectorModalBottomSheet(
onDismiss = {},
state = state,
feeSelectorIntents = StubFeeSelectorIntents(),
feeDisplaySource = FeeSelectorParams.FeeDisplaySource.BottomSheet,
)
}
}

View file

@ -41,12 +41,14 @@ internal fun getWcCommonScreen(
val state = model.uiState.value ?: error("in this step state should be not null")
feeSelectorComponentFactory.create(
context = appComponentContext,
onDismiss = model::popBack,
params = FeeSelectorParams.FeeSelectorDetailsParams(
state = state.feeSelectorUM,
onLoadFee = model::loadFee,
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
callback = model,
suggestedFeeState = model.suggestedFeeState,
feeDisplaySource = FeeSelectorParams.FeeDisplaySource.BottomSheet,
),
)
}

View file

@ -26,9 +26,10 @@ internal class WcSendTransactionComponent(
state = state.feeSelectorUM,
onLoadFee = model::loadFee,
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
callback = model,
suggestedFeeState = model.suggestedFeeState,
feeDisplaySource = FeeSelectorParams.FeeDisplaySource.BottomSheet,
),
onResult = model::updateFee,
)
}

View file

@ -7,4 +7,6 @@ internal interface WcCommonTransactionModel {
val uiState: StateFlow<WcCommonTransactionUM?>
fun dismiss()
fun popBack()
}

View file

@ -67,6 +67,10 @@ internal class WcAddNetworkModel @Inject constructor(
_uiState.value?.transaction?.onDismiss?.invoke() ?: router.pop()
}
override fun popBack() {
router.pop()
}
fun showTransactionRequest() {
stackNavigation.pushNew(WcTransactionRoutes.TransactionRequestInfo)
}

View file

@ -118,7 +118,20 @@ internal class WcSendTransactionModel @Inject constructor(
}
}
/**
* Handles callback with updated [feeSelectorUM] from click FeeSelectorComponent.
* We need to trigger navigation to dismiss fee selector component
*/
override fun onFeeResult(feeSelectorUM: FeeSelectorUM) {
updateFee(feeSelectorUM)
popBack()
}
/**
* Handles fee updates.
* Also handles fee results from FeeSelectorBlockComponent
*/
fun updateFee(feeSelectorUM: FeeSelectorUM) {
_uiState.update { it?.copy(feeSelectorUM = feeSelectorUM) }
val fee = (feeSelectorUM as? FeeSelectorUM.Content)?.selectedFeeItem?.fee ?: return
(useCase as? WcMutableFee)?.updateFee(fee)
@ -186,6 +199,10 @@ internal class WcSendTransactionModel @Inject constructor(
_uiState.value?.transaction?.onDismiss?.invoke() ?: router.pop()
}
override fun popBack() {
stackNavigation.pop()
}
fun showTransactionRequest() {
stackNavigation.pushNew(WcTransactionRoutes.TransactionRequestInfo)
}

View file

@ -2,6 +2,7 @@ package com.tangem.features.walletconnect.transaction.model
import androidx.compose.runtime.Stable
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.pushNew
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
@ -76,6 +77,10 @@ internal class WcSignTransactionModel @Inject constructor(
_uiState.value?.transaction?.onDismiss?.invoke() ?: router.pop()
}
override fun popBack() {
stackNavigation.pop()
}
fun showTransactionRequest() {
stackNavigation.pushNew(WcTransactionRoutes.TransactionRequestInfo)
}