Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-26 10:37:53 +05:00
parent 94af682f08
commit a284c8d311
13 changed files with 236 additions and 29 deletions

View file

@ -5,4 +5,5 @@ interface OnrampIntents {
fun openSettings()
fun openCurrenciesList()
fun onBuyClick()
fun openProviders()
}

View file

@ -45,6 +45,7 @@ internal sealed interface OnrampMainComponentUM {
override val topBarConfig: OnrampMainTopBarUM,
override val buyButtonConfig: BuyButtonConfig,
val amountBlockState: OnrampAmountBlockUM,
val providerBlockState: OnrampProviderBlockUM,
) : OnrampMainComponentUM
}

View file

@ -0,0 +1,13 @@
package com.tangem.features.onramp.main.entity
sealed class OnrampProviderBlockUM {
data object Empty : OnrampProviderBlockUM()
data object Loading : OnrampProviderBlockUM()
data class Content(
val paymentMethodIconUrl: String,
val paymentMethodName: String,
val providerName: String,
val isBestRate: Boolean,
val onClick: () -> Unit,
) : OnrampProviderBlockUM()
}

View file

@ -42,6 +42,7 @@ internal class OnrampStateFactory(
topBarConfig = state.topBarConfig.copy(endButtonUM = endButton),
buyButtonConfig = state.buyButtonConfig,
amountBlockState = getInitialAmountBlockState(currency),
providerBlockState = OnrampProviderBlockUM.Empty,
)
}

View file

@ -6,6 +6,7 @@ import androidx.compose.ui.text.input.KeyboardType
import com.tangem.core.ui.extensions.TextReference
import com.tangem.features.onramp.main.entity.OnrampAmountSecondaryFieldUM
import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
import com.tangem.features.onramp.main.entity.OnrampProviderBlockUM
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import com.tangem.utils.isNullOrZero
@ -39,6 +40,7 @@ internal class OnrampAmountFieldChangeConverter(
amountFieldModel = amountFieldModel,
secondaryFieldModel = OnrampAmountSecondaryFieldUM.Loading,
),
providerBlockState = OnrampProviderBlockUM.Loading,
)
}
@ -59,6 +61,7 @@ internal class OnrampAmountFieldChangeConverter(
amountFieldModel = amountFieldModel,
secondaryFieldModel = OnrampAmountSecondaryFieldUM.Content(TextReference.EMPTY),
),
providerBlockState = OnrampProviderBlockUM.Empty,
)
}
}

View file

@ -10,12 +10,13 @@ import com.tangem.domain.onramp.model.OnrampCurrency
import com.tangem.domain.onramp.model.OnrampQuote
import com.tangem.domain.tokens.model.AmountType
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.main.entity.OnrampAmountBlockUM
import com.tangem.features.onramp.main.entity.OnrampAmountSecondaryFieldUM
import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
import com.tangem.features.onramp.main.entity.*
import com.tangem.utils.Provider
internal class OnrampAmountStateFactory(private val currentStateProvider: Provider<OnrampMainComponentUM>) {
internal class OnrampAmountStateFactory(
private val currentStateProvider: Provider<OnrampMainComponentUM>,
private val onrampIntents: OnrampIntents,
) {
private val onrampAmountFieldChangeConverter = OnrampAmountFieldChangeConverter(
currentStateProvider = currentStateProvider,
@ -64,14 +65,25 @@ internal class OnrampAmountStateFactory(private val currentStateProvider: Provid
val amountState = currentState.amountBlockState
return currentState.copy(
amountBlockState = amountState.copy(
secondaryFieldModel = quote.toUiModel(amountState),
secondaryFieldModel = quote.toSecondaryFieldUiModel(amountState),
),
providerBlockState = quote.toProviderBlockState(),
)
}
private fun OnrampQuote.toUiModel(amountState: OnrampAmountBlockUM): OnrampAmountSecondaryFieldUM {
private fun OnrampQuote.toProviderBlockState(): OnrampProviderBlockUM {
return OnrampProviderBlockUM.Content(
paymentMethodIconUrl = paymentMethod.imageUrl,
paymentMethodName = paymentMethod.name,
providerName = provider.info.name,
isBestRate = true,
onClick = onrampIntents::openProviders,
)
}
private fun OnrampQuote.toSecondaryFieldUiModel(amountState: OnrampAmountBlockUM): OnrampAmountSecondaryFieldUM {
return when (this) {
is OnrampQuote.Content -> {
is OnrampQuote.Data -> {
val amount = toAmount.format {
crypto(
symbol = amountState.amountFieldModel.cryptoAmount.currencySymbol,

View file

@ -41,7 +41,10 @@ internal class OnrampMainComponentModel @Inject constructor(
cryptoCurrency = params.cryptoCurrency,
onrampIntents = this,
)
private val amountStateFactory = OnrampAmountStateFactory(currentStateProvider = Provider { _state.value })
private val amountStateFactory = OnrampAmountStateFactory(
currentStateProvider = Provider { _state.value },
onrampIntents = this,
)
private val _state: MutableStateFlow<OnrampMainComponentUM> = MutableStateFlow(
value = stateFactory.getInitialState(
@ -130,6 +133,9 @@ internal class OnrampMainComponentModel @Inject constructor(
bottomSheetNavigation.activate(OnrampMainBottomSheetConfig.CurrenciesList)
}
override fun openProviders() {
}
override fun onDestroy() {
modelScope.launch { clearOnrampCacheUseCase.invoke() }
super.onDestroy()

View file

@ -93,8 +93,10 @@ private fun Content(state: OnrampMainComponentUM.Content, modifier: Modifier = M
.verticalScroll(rememberScrollState())
.navigationBarsPadding()
.padding(bottom = TangemTheme.dimens.spacing76),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
OnrampAmountContent(state = state.amountBlockState)
OnrampProviderContent(state = state.providerBlockState, modifier = Modifier.fillMaxWidth())
}
}

View file

@ -0,0 +1,133 @@
package com.tangem.features.onramp.main.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.extensions.appendSpace
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.main.entity.OnrampProviderBlockUM
@Composable
internal fun OnrampProviderContent(state: OnrampProviderBlockUM, modifier: Modifier = Modifier) {
when (state) {
is OnrampProviderBlockUM.Empty -> Unit
is OnrampProviderBlockUM.Loading -> OnrampProviderLoading(modifier)
is OnrampProviderBlockUM.Content -> OnrampProviderBlock(modifier = modifier, state = state)
}
}
@Composable
private fun OnrampProviderBlock(state: OnrampProviderBlockUM.Content, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.clip(shape = RoundedCornerShape(size = TangemTheme.dimens.radius16))
.background(TangemTheme.colors.background.action)
.clickable(onClick = state.onClick)
.padding(TangemTheme.dimens.spacing12),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
) {
SubcomposeAsyncImage(
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing12)
.size(size = TangemTheme.dimens.size40)
.clip(TangemTheme.shapes.roundedCorners8),
model = ImageRequest.Builder(context = LocalContext.current)
.data(state.paymentMethodIconUrl)
.crossfade(enable = true)
.allowHardware(false)
.build(),
loading = { RectangleShimmer(radius = TangemTheme.dimens.radius8) },
contentDescription = null,
)
Column(modifier = Modifier.weight(1F)) {
Text(
text = buildAnnotatedString {
append(stringResource(id = R.string.onramp_pay_with))
appendSpace()
withStyle(
style = SpanStyle(
fontWeight = TangemTheme.typography.subtitle2.fontWeight,
color = TangemTheme.colors.text.primary1,
),
) {
append(state.paymentMethodName)
}
},
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.body2,
)
Text(
text = buildAnnotatedString {
append(stringResource(id = R.string.onramp_via))
appendSpace()
append(state.providerName)
},
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
Text(
modifier = Modifier
.background(
color = TangemTheme.colors.icon.accent,
shape = RoundedCornerShape(TangemTheme.dimens.radius4),
)
.padding(
horizontal = TangemTheme.dimens.spacing6,
vertical = TangemTheme.dimens.spacing1,
),
text = stringResource(id = R.string.express_provider_best_rate),
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.primary2,
)
}
}
@Composable
private fun OnrampProviderLoading(modifier: Modifier = Modifier) {
Column(
modifier = modifier
.clip(shape = RoundedCornerShape(size = TangemTheme.dimens.radius16))
.background(TangemTheme.colors.background.action)
.padding(TangemTheme.dimens.spacing12),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
) {
Text(
text = stringResource(id = R.string.express_provider),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
) {
CircularProgressIndicator(
color = TangemTheme.colors.icon.informative,
strokeWidth = TangemTheme.dimens.size2,
modifier = Modifier.size(TangemTheme.dimens.size16),
)
Text(
text = stringResource(id = R.string.express_fetch_best_rates),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
}
}
}