Updated on 2026-08-14
This commit is contained in:
parent
56157412ac
commit
55ffa595d7
29 changed files with 40 additions and 223 deletions
|
|
@ -15,6 +15,7 @@ dependencies {
|
|||
|
||||
/** Domain models */
|
||||
api(projects.domain.models)
|
||||
implementation(projects.domain.appCurrency.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.nft.models)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.features.send.v2.api
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
||||
interface FeeSelectorComponent : ComposableContentComponent, ComposableBottomSheetComponent {
|
||||
data class Params(val appCurrency: AppCurrency)
|
||||
|
||||
interface Factory : ComponentFactory<Params, FeeSelectorComponent>
|
||||
}
|
||||
|
|
@ -16,7 +16,6 @@ dependencies {
|
|||
implementation(projects.features.sendV2.api)
|
||||
implementation(projects.features.txhistory.api)
|
||||
implementation(projects.features.nft.api)
|
||||
implementation(projects.features.feeSelector.api)
|
||||
|
||||
/** Libs */
|
||||
implementation(projects.libs.crypto)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tangem.features.send.v2.feeselector
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
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.features.send.v2.api.FeeSelectorComponent
|
||||
import com.tangem.features.send.v2.feeselector.model.FeeSelectorModel
|
||||
import com.tangem.features.send.v2.feeselector.ui.FeeSelectorModalBottomSheet
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultFeeSelectorComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted private val params: FeeSelectorComponent.Params,
|
||||
) : FeeSelectorComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: FeeSelectorModel = getOrCreateModel(params = params)
|
||||
|
||||
override fun dismiss() {
|
||||
model.dismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
FeeSelectorModalBottomSheet(onDismiss = ::dismiss, state = TODO())
|
||||
}
|
||||
|
||||
// Temporary workaround, to use test this component
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
BackHandler(onBack = router::pop)
|
||||
FeeSelectorModalBottomSheet(onDismiss = ::dismiss, state = state)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : FeeSelectorComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: FeeSelectorComponent.Params,
|
||||
): DefaultFeeSelectorComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.features.send.v2.feeselector.di
|
||||
|
||||
import com.tangem.features.send.v2.api.FeeSelectorComponent
|
||||
import com.tangem.features.send.v2.feeselector.DefaultFeeSelectorComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface FeeSelectorFeatureModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindComponentFactory(factory: DefaultFeeSelectorComponent.Factory): FeeSelectorComponent.Factory
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.features.send.v2.feeselector.di
|
||||
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.send.v2.feeselector.model.FeeSelectorModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface FeeSelectorModelModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(FeeSelectorModel::class)
|
||||
fun bindModel(model: FeeSelectorModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.features.send.v2.feeselector.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Immutable
|
||||
internal sealed class FeeSelectorUM {
|
||||
|
||||
data object Loading : FeeSelectorUM()
|
||||
|
||||
data class Content(
|
||||
val isDoneEnabled: Boolean,
|
||||
val feeItems: ImmutableList<FeeItem>,
|
||||
val selectedFeeItem: FeeItem,
|
||||
val isFeeApproximate: Boolean,
|
||||
val feeFiatRateDataHolder: FeeFiatRateDataHolder?,
|
||||
) : FeeSelectorUM()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class FeeFiatRateDataHolder(
|
||||
val rate: BigDecimal,
|
||||
val appCurrency: AppCurrency,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
internal sealed class FeeItem {
|
||||
abstract val onSelect: () -> Unit
|
||||
|
||||
data class Suggested(val title: TextReference, val amount: Amount, override val onSelect: () -> Unit) : FeeItem()
|
||||
data class Slow(val amount: Amount, override val onSelect: () -> Unit) : FeeItem()
|
||||
data class Market(val amount: Amount, override val onSelect: () -> Unit) : FeeItem()
|
||||
data class Fast(val amount: Amount, override val onSelect: () -> Unit) : FeeItem()
|
||||
data class Custom(val customValues: ImmutableList<CustomFeeFieldUM>, override val onSelect: () -> Unit) : FeeItem()
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.features.send.v2.feeselector.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
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.features.send.v2.api.FeeSelectorComponent
|
||||
import com.tangem.features.send.v2.feeselector.entity.FeeSelectorUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class FeeSelectorModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
private val router: Router,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private val params = paramsContainer.require<FeeSelectorComponent.Params>()
|
||||
val uiState: StateFlow<FeeSelectorUM>
|
||||
field = MutableStateFlow<FeeSelectorUM>(FeeSelectorUM.Loading)
|
||||
|
||||
fun dismiss() {
|
||||
router.pop()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,512 @@
|
|||
package com.tangem.features.send.v2.feeselector.ui
|
||||
|
||||
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
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
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.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Devices
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.ui.amountScreen.utils.getFiatReference
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.atoms.text.EllipsisText
|
||||
import com.tangem.core.ui.components.atoms.text.TextEllipsis
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetWithFooter
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnterInfoAmountV2
|
||||
import com.tangem.core.ui.extensions.*
|
||||
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.feeselector.entity.FeeFiatRateDataHolder
|
||||
import com.tangem.features.send.v2.feeselector.entity.FeeItem
|
||||
import com.tangem.features.send.v2.feeselector.entity.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import com.tangem.utils.StringsSigns
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Composable
|
||||
internal fun FeeSelectorModalBottomSheet(state: FeeSelectorUM, onDismiss: () -> Unit) {
|
||||
if (state !is FeeSelectorUM.Content) return
|
||||
|
||||
TangemModalBottomSheetWithFooter<TangemBottomSheetConfigContent.Empty>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = onDismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
title = resourceReference(R.string.common_network_fee_title),
|
||||
startIconRes = R.drawable.ic_back_24,
|
||||
onStartClick = onDismiss,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
FeeSelectorItems(state = state, modifier = Modifier.padding(vertical = 4.dp, horizontal = 16.dp))
|
||||
},
|
||||
footer = {
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
text = stringResourceSafe(R.string.common_done),
|
||||
onClick = onDismiss,
|
||||
enabled = state.isDoneEnabled,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
private fun FeeSelectorItems(state: FeeSelectorUM.Content, modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
state.feeItems.fastForEachIndexed { index, item ->
|
||||
val isSelected = item == state.selectedFeeItem
|
||||
val lastItem = index == state.feeItems.size - 1
|
||||
val iconTint by animateColorAsState(
|
||||
targetValue = if (isSelected) TangemTheme.colors.icon.accent else TangemTheme.colors.text.tertiary,
|
||||
label = "Fee selector icon tint change",
|
||||
)
|
||||
val iconBackgroundColor by animateColorAsState(
|
||||
targetValue = if (isSelected) {
|
||||
TangemTheme.colors.icon.accent.copy(alpha = 0.1F)
|
||||
} else {
|
||||
TangemTheme.colors.background.secondary
|
||||
},
|
||||
label = "Fee selector icon background change",
|
||||
)
|
||||
val onSelect by rememberUpdatedState(item.onSelect)
|
||||
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
|
||||
},
|
||||
)
|
||||
.clickableSingle(onClick = onSelect)
|
||||
when (item) {
|
||||
is FeeItem.Suggested -> RegularFeeItemContent(
|
||||
modifier = itemModifier,
|
||||
title = item.title,
|
||||
iconRes = R.drawable.ic_star_mini_24,
|
||||
iconBackgroundColor = iconBackgroundColor,
|
||||
iconTint = iconTint,
|
||||
preDot = stringReference(
|
||||
item.amount.value.format {
|
||||
crypto(
|
||||
symbol = item.amount.currencySymbol,
|
||||
decimals = item.amount.decimals,
|
||||
).fee(canBeLower = state.isFeeApproximate)
|
||||
},
|
||||
),
|
||||
postDot = if (state.feeFiatRateDataHolder != null) {
|
||||
getFiatReference(
|
||||
value = item.amount.value,
|
||||
rate = state.feeFiatRateDataHolder.rate,
|
||||
appCurrency = state.feeFiatRateDataHolder.appCurrency,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
ellipsizeOffset = item.amount.currencySymbol.length,
|
||||
showDivider = !isSelected && !lastItem,
|
||||
)
|
||||
is FeeItem.Slow -> RegularFeeItemContent(
|
||||
modifier = itemModifier,
|
||||
title = resourceReference(R.string.common_fee_selector_option_slow),
|
||||
iconRes = R.drawable.ic_tortoise_24,
|
||||
iconBackgroundColor = iconBackgroundColor,
|
||||
iconTint = iconTint,
|
||||
preDot = stringReference(
|
||||
item.amount.value.format {
|
||||
crypto(
|
||||
symbol = item.amount.currencySymbol,
|
||||
decimals = item.amount.decimals,
|
||||
).fee(canBeLower = state.isFeeApproximate)
|
||||
},
|
||||
),
|
||||
postDot = if (state.feeFiatRateDataHolder != null) {
|
||||
getFiatReference(
|
||||
value = item.amount.value,
|
||||
rate = state.feeFiatRateDataHolder.rate,
|
||||
appCurrency = state.feeFiatRateDataHolder.appCurrency,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
ellipsizeOffset = item.amount.currencySymbol.length,
|
||||
showDivider = !isSelected && !lastItem,
|
||||
)
|
||||
is FeeItem.Market -> RegularFeeItemContent(
|
||||
modifier = itemModifier,
|
||||
title = resourceReference(R.string.common_fee_selector_option_market),
|
||||
iconRes = R.drawable.ic_bird_24,
|
||||
iconBackgroundColor = iconBackgroundColor,
|
||||
iconTint = iconTint,
|
||||
preDot = stringReference(
|
||||
item.amount.value.format {
|
||||
crypto(
|
||||
symbol = item.amount.currencySymbol,
|
||||
decimals = item.amount.decimals,
|
||||
).fee(canBeLower = state.isFeeApproximate)
|
||||
},
|
||||
),
|
||||
postDot = if (state.feeFiatRateDataHolder != null) {
|
||||
getFiatReference(
|
||||
value = item.amount.value,
|
||||
rate = state.feeFiatRateDataHolder.rate,
|
||||
appCurrency = state.feeFiatRateDataHolder.appCurrency,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
ellipsizeOffset = item.amount.currencySymbol.length,
|
||||
showDivider = !isSelected && !lastItem,
|
||||
)
|
||||
is FeeItem.Fast -> RegularFeeItemContent(
|
||||
modifier = itemModifier,
|
||||
title = resourceReference(R.string.common_fee_selector_option_fast),
|
||||
iconRes = R.drawable.ic_hare_24,
|
||||
iconBackgroundColor = iconBackgroundColor,
|
||||
iconTint = iconTint,
|
||||
preDot = stringReference(
|
||||
item.amount.value.format {
|
||||
crypto(
|
||||
symbol = item.amount.currencySymbol,
|
||||
decimals = item.amount.decimals,
|
||||
).fee(canBeLower = state.isFeeApproximate)
|
||||
},
|
||||
),
|
||||
postDot = if (state.feeFiatRateDataHolder != null) {
|
||||
getFiatReference(
|
||||
value = item.amount.value,
|
||||
rate = state.feeFiatRateDataHolder.rate,
|
||||
appCurrency = state.feeFiatRateDataHolder.appCurrency,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
ellipsizeOffset = item.amount.currencySymbol.length,
|
||||
showDivider = !isSelected && !lastItem,
|
||||
)
|
||||
is FeeItem.Custom -> CustomFeeBlock(
|
||||
modifier = itemModifier,
|
||||
customFee = item,
|
||||
isSelected = isSelected,
|
||||
iconBackgroundColor = iconBackgroundColor,
|
||||
iconTint = iconTint,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CustomFeeBlock(
|
||||
customFee: FeeItem.Custom,
|
||||
isSelected: Boolean,
|
||||
iconBackgroundColor: Color,
|
||||
iconTint: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
modifier = Modifier.padding(all = 12.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.background(color = iconBackgroundColor, shape = CircleShape)
|
||||
.padding(6.dp),
|
||||
painter = painterResource(R.drawable.ic_edit_v2_24),
|
||||
tint = iconTint,
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.common_custom),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = isSelected,
|
||||
label = "Custom Fee Selected Animation",
|
||||
enter = expandVertically().plus(fadeIn()),
|
||||
exit = shrinkVertically().plus(fadeOut()),
|
||||
) {
|
||||
ExpandedCustomFeeItems(customFeeFields = customFee.customValues, onValueChange = { _, _ -> })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExpandedCustomFeeItems(
|
||||
customFeeFields: ImmutableList<CustomFeeFieldUM>,
|
||||
onValueChange: (Int, String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
customFeeFields.fastForEachIndexed { index, field ->
|
||||
val showDivider = index != customFeeFields.size - 1
|
||||
if (field.label != null) {
|
||||
InputRowEnterInfoAmountV2(
|
||||
text = field.value,
|
||||
decimals = field.decimals,
|
||||
symbol = field.symbol,
|
||||
title = field.title,
|
||||
titleColor = TangemTheme.colors.text.tertiary,
|
||||
info = field.label,
|
||||
keyboardOptions = field.keyboardOptions,
|
||||
keyboardActions = field.keyboardActions,
|
||||
onValueChange = { onValueChange(index, it) },
|
||||
showDivider = showDivider,
|
||||
isReadOnly = field.isReadonly,
|
||||
)
|
||||
} else {
|
||||
InputRowEnterInfoAmountV2(
|
||||
text = field.value,
|
||||
decimals = field.decimals,
|
||||
title = field.title,
|
||||
titleColor = TangemTheme.colors.text.tertiary,
|
||||
symbol = field.symbol,
|
||||
onValueChange = { onValueChange(index, it) },
|
||||
keyboardOptions = field.keyboardOptions,
|
||||
keyboardActions = field.keyboardActions,
|
||||
showDivider = showDivider,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RegularFeeItemContent(
|
||||
title: TextReference,
|
||||
@DrawableRes iconRes: Int,
|
||||
iconBackgroundColor: Color,
|
||||
iconTint: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
preDot: TextReference? = null,
|
||||
postDot: TextReference? = null,
|
||||
ellipsizeOffset: Int? = null,
|
||||
showDivider: Boolean = true,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
modifier = Modifier.padding(all = 12.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.background(color = iconBackgroundColor, shape = CircleShape)
|
||||
.padding(6.dp),
|
||||
painter = painterResource(iconRes),
|
||||
tint = iconTint,
|
||||
contentDescription = null,
|
||||
)
|
||||
FeeDescription(
|
||||
title = title,
|
||||
preDot = preDot,
|
||||
postDot = postDot,
|
||||
ellipsizeOffset = ellipsizeOffset,
|
||||
)
|
||||
}
|
||||
if (showDivider) {
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(start = 60.dp, end = 12.dp),
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = 0.5.dp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeeDescription(
|
||||
title: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
preDot: TextReference? = null,
|
||||
postDot: TextReference? = null,
|
||||
ellipsizeOffset: Int? = null,
|
||||
) {
|
||||
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
if (preDot != null) {
|
||||
FeeValueContent(preDot = preDot, postDot = postDot, ellipsizeOffset = ellipsizeOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeeValueContent(preDot: TextReference, postDot: TextReference?, ellipsizeOffset: Int? = null) {
|
||||
val ellipsis = if (ellipsizeOffset == null) {
|
||||
TextEllipsis.End
|
||||
} else {
|
||||
TextEllipsis.OffsetEnd(ellipsizeOffset)
|
||||
}
|
||||
val textColor = TangemTheme.colors.text.tertiary
|
||||
val textStyle = TangemTheme.typography.caption1
|
||||
Row {
|
||||
EllipsisText(
|
||||
text = preDot.resolveReference(),
|
||||
style = textStyle,
|
||||
color = textColor,
|
||||
textAlign = TextAlign.End,
|
||||
ellipsis = ellipsis,
|
||||
)
|
||||
if (postDot != null) {
|
||||
Text(
|
||||
text = StringsSigns.DOT,
|
||||
style = textStyle,
|
||||
color = textColor,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing4),
|
||||
)
|
||||
Text(text = postDot.resolveReference(), style = textStyle, color = textColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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 FeeSelectorBS_Preview(
|
||||
@PreviewParameter(FeeSelectorUMContentProvider::class)
|
||||
state: FeeSelectorUM.Content,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
FeeSelectorModalBottomSheet(onDismiss = {}, state = state)
|
||||
}
|
||||
}
|
||||
|
||||
private class FeeSelectorUMContentProvider : CollectionPreviewParameterProvider<FeeSelectorUM.Content>(
|
||||
collection = listOf(
|
||||
FeeSelectorUM.Content(
|
||||
isDoneEnabled = false,
|
||||
feeItems = persistentListOf(
|
||||
FeeItem.Suggested(
|
||||
title = stringReference("Suggested by Tangem"),
|
||||
amount = Amount(value = BigDecimal("0.1"), blockchain = Blockchain.Ethereum),
|
||||
onSelect = onSelectStub,
|
||||
),
|
||||
FeeItem.Slow(
|
||||
amount = Amount(value = BigDecimal("0.01"), blockchain = Blockchain.Ethereum),
|
||||
onSelect = onSelectStub,
|
||||
),
|
||||
FeeItem.Market(
|
||||
amount = Amount(value = BigDecimal("0.02"), blockchain = Blockchain.Ethereum),
|
||||
onSelect = onSelectStub,
|
||||
),
|
||||
FeeItem.Fast(
|
||||
amount = Amount(value = BigDecimal("0.3"), blockchain = Blockchain.Ethereum),
|
||||
onSelect = onSelectStub,
|
||||
),
|
||||
FeeItem.Custom(customValues = customFeeFields, onSelect = onSelectStub),
|
||||
),
|
||||
// selectedFeeItem = FeeItem.Market(
|
||||
// amount = Amount(value = BigDecimal("0.02"), blockchain = Blockchain.Ethereum),
|
||||
// onSelect = onSelectStub
|
||||
// ),
|
||||
selectedFeeItem = FeeItem.Custom(customValues = customFeeFields, onSelect = onSelectStub),
|
||||
isFeeApproximate = true,
|
||||
feeFiatRateDataHolder = FeeFiatRateDataHolder(
|
||||
rate = BigDecimal.TEN,
|
||||
appCurrency = AppCurrency.Default,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
private val onSelectStub: () -> Unit = {}
|
||||
|
||||
private val customFeeFields = persistentListOf(
|
||||
CustomFeeFieldUM(
|
||||
value = "0.119806",
|
||||
onValueChange = {},
|
||||
keyboardOptions = KeyboardOptions(),
|
||||
keyboardActions = KeyboardActions(),
|
||||
symbol = "ETH",
|
||||
decimals = 8,
|
||||
title = resourceReference(R.string.send_max_fee),
|
||||
footer = resourceReference(R.string.send_custom_amount_fee_footer),
|
||||
label = stringReference("~ 0,03 \$"),
|
||||
isReadonly = false,
|
||||
),
|
||||
CustomFeeFieldUM(
|
||||
value = "40000",
|
||||
onValueChange = {},
|
||||
keyboardOptions = KeyboardOptions(),
|
||||
keyboardActions = KeyboardActions(),
|
||||
symbol = "GWEI",
|
||||
decimals = 8,
|
||||
title = resourceReference(R.string.send_gas_price),
|
||||
footer = resourceReference(R.string.send_gas_price_footer),
|
||||
label = null,
|
||||
isReadonly = false,
|
||||
),
|
||||
CustomFeeFieldUM(
|
||||
value = "31400",
|
||||
onValueChange = {},
|
||||
keyboardOptions = KeyboardOptions(),
|
||||
keyboardActions = KeyboardActions(),
|
||||
symbol = null,
|
||||
decimals = 8,
|
||||
title = resourceReference(R.string.send_gas_limit),
|
||||
footer = resourceReference(R.string.send_gas_limit_footer),
|
||||
label = null,
|
||||
isReadonly = false,
|
||||
),
|
||||
)
|
||||
|
|
@ -4,8 +4,8 @@ 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.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.feeselector.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ 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.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.feeselector.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.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.converters.custom
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.features.feeselector.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ import com.tangem.core.ui.utils.parseBigDecimal
|
|||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.feeselector.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
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.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.features.feeselector.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.CustomFeeConverter
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.feeselector.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import com.tangem.core.ui.utils.parseBigDecimal
|
|||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.feeselector.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance
|
||||
|
|
@ -20,6 +19,7 @@ import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.eth
|
|||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.GAS_DECIMALS
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.GIGA_DECIMALS
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.setEmpty
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import com.tangem.core.ui.utils.parseBigDecimal
|
|||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.feeselector.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance
|
||||
|
|
@ -20,6 +19,7 @@ import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.eth
|
|||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.GAS_DECIMALS
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.GIGA_DECIMALS
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.setEmpty
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ import com.tangem.core.ui.utils.parseBigDecimal
|
|||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.feeselector.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.CustomFeeConverter
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import com.tangem.core.ui.components.containers.FooterContainer
|
|||
import com.tangem.core.ui.components.inputrow.InputRowEnterAmount
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnterInfoAmount
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feeselector.api.entity.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.CustomFeeFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.send.v2.subcomponents.fee.ui.state
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
@Immutable
|
||||
data class CustomFeeFieldUM(
|
||||
val value: String,
|
||||
val onValueChange: (String) -> Unit,
|
||||
val keyboardOptions: KeyboardOptions,
|
||||
val keyboardActions: KeyboardActions,
|
||||
val symbol: String?,
|
||||
val decimals: Int,
|
||||
val title: TextReference,
|
||||
val footer: TextReference,
|
||||
val label: TextReference? = null,
|
||||
val isReadonly: Boolean = false,
|
||||
)
|
||||
|
|
@ -5,7 +5,6 @@ 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.feeselector.api.entity.CustomFeeFieldUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ 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.feeselector.api.entity.CustomFeeFieldUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue