Updated on 2026-08-14
This commit is contained in:
parent
49800731e3
commit
d664e6ecbb
13 changed files with 267 additions and 39 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||
import com.tangem.datasource.local.swap.DefaultSwapBestRateAnimationStore
|
||||
import com.tangem.datasource.local.swap.DefaultSwapTransactionStatusStore
|
||||
import com.tangem.datasource.local.swap.SwapBestRateAnimationStore
|
||||
|
|
@ -22,4 +23,12 @@ object SwapStoreModule {
|
|||
dataStore = RuntimeDataStore(),
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSwapBestRateAnimationStore(): SwapBestRateAnimationStore {
|
||||
return DefaultSwapBestRateAnimationStore(
|
||||
dataStore = RuntimeSharedStore(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.datasource.local.swap
|
||||
|
||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||
|
||||
internal class DefaultSwapBestRateAnimationStore(
|
||||
private val dataStore: RuntimeSharedStore<Boolean>,
|
||||
) : SwapBestRateAnimationStore, RuntimeSharedStore<Boolean> by dataStore {
|
||||
/**
|
||||
* Returns flag indicating whether should show best rate animation in current session.
|
||||
* Animation should appear once per session
|
||||
*
|
||||
* If true, reset flag to false
|
||||
*/
|
||||
override suspend fun getSyncOrNull(): Boolean {
|
||||
val value = dataStore.getSyncOrNull() ?: true
|
||||
if (value) {
|
||||
dataStore.store(false)
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.datasource.local.swap
|
||||
|
||||
/**
|
||||
* Stores flag indicating whether should show best rate animation in current session.
|
||||
* Animation should appear once per session
|
||||
*
|
||||
* If true, reset flag to false
|
||||
*/
|
||||
interface SwapBestRateAnimationStore {
|
||||
suspend fun getSyncOrNull(): Boolean
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ dependencies {
|
|||
implementation(projects.core.ui)
|
||||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.configToggles)
|
||||
implementation(projects.core.datasource)
|
||||
|
||||
/** Common */
|
||||
implementation(projects.common.ui)
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ internal class SwapAmountBlockComponent(
|
|||
onInfoClick = model::onInfoClick,
|
||||
isClickEnabled = isClickEnabled,
|
||||
onClick = onClick,
|
||||
onFinishAnimation = model::onFinishAnimation,
|
||||
onProviderSelectClick = {
|
||||
val amountUM = model.uiState.value as? SwapAmountUM.Content ?: return@SwapAmountBlockContent
|
||||
val selectedProvider = amountUM.selectedQuote.provider ?: return@SwapAmountBlockContent
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ internal sealed class SwapAmountUM {
|
|||
|
||||
// extra data
|
||||
val appCurrency: AppCurrency?,
|
||||
val showBestRateAnimation: Boolean,
|
||||
) : SwapAmountUM()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ 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.extensions.resourceReference
|
||||
import com.tangem.datasource.local.swap.SwapBestRateAnimationStore
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.express.models.ExpressError
|
||||
|
|
@ -73,6 +74,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
private val getAllowanceUseCase: GetAllowanceUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getUserCountryUseCase: GetUserCountryUseCase,
|
||||
private val swapBestRateAnimationStore: SwapBestRateAnimationStore,
|
||||
private val appRouter: AppRouter,
|
||||
private val swapAmountAlertFactory: SwapAmountAlertFactory,
|
||||
private val swapAlertFactory: SwapAlertFactory,
|
||||
|
|
@ -96,6 +98,8 @@ internal class SwapAmountModel @Inject constructor(
|
|||
var userCountry: UserCountry = UserCountry.Other(Locale.getDefault().country)
|
||||
val bottomSheetNavigation: SlotNavigation<SwapChooseProviderConfig> = SlotNavigation()
|
||||
|
||||
var showBestRateAnimation: Boolean = false
|
||||
|
||||
val uiState: StateFlow<SwapAmountUM>
|
||||
field = MutableStateFlow(params.amountUM)
|
||||
|
||||
|
|
@ -107,6 +111,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }
|
||||
userCountry = getUserCountryUseCase.invokeSync().getOrNull()
|
||||
?: UserCountry.Other(Locale.getDefault().country)
|
||||
showBestRateAnimation = swapBestRateAnimationStore.getSyncOrNull()
|
||||
}
|
||||
configAmountNavigation()
|
||||
subscribeOnCryptoCurrencyStatusFlow()
|
||||
|
|
@ -245,6 +250,12 @@ internal class SwapAmountModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onFinishAnimation() {
|
||||
uiState.update {
|
||||
(it as? SwapAmountUM.Content)?.copy(showBestRateAnimation = false) ?: it
|
||||
}
|
||||
}
|
||||
|
||||
private fun confirmSendWithSwapClose() {
|
||||
val amountParams = params as? SwapAmountComponentParams.AmountParams ?: return
|
||||
val amountFieldData = uiState.value.primaryAmount.amountField as? AmountState.Data
|
||||
|
|
@ -259,6 +270,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
swapDirection = swapDirection,
|
||||
clickIntents = this,
|
||||
isBalanceHidden = params.isBalanceHidingFlow.value,
|
||||
showBestRateAnimation = showBestRateAnimation,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -292,6 +304,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
swapDirection = swapDirection,
|
||||
clickIntents = this,
|
||||
isBalanceHidden = params.isBalanceHidingFlow.value,
|
||||
showBestRateAnimation = showBestRateAnimation,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -399,6 +412,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
swapDirection = swapDirection,
|
||||
clickIntents = this@SwapAmountModel,
|
||||
isBalanceHidden = params.isBalanceHidingFlow.value,
|
||||
showBestRateAnimation = showBestRateAnimation,
|
||||
),
|
||||
)
|
||||
startLoadingQuotesTask(isSilentReload = false)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ internal class SwapAmountPrimaryReadyStateTransformer(
|
|||
private val clickIntents: AmountScreenClickIntents,
|
||||
private val swapDirection: SwapDirection,
|
||||
private val isBalanceHidden: Boolean,
|
||||
private val showBestRateAnimation: Boolean,
|
||||
) : Transformer<SwapAmountUM> {
|
||||
|
||||
private val amountFieldConverter = SwapAmountFieldConverter(
|
||||
|
|
@ -52,6 +53,7 @@ internal class SwapAmountPrimaryReadyStateTransformer(
|
|||
swapQuotes = persistentListOf(),
|
||||
selectedQuote = SwapQuoteUM.Empty,
|
||||
appCurrency = appCurrency,
|
||||
showBestRateAnimation = showBestRateAnimation,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ internal class SwapAmountSecondaryReadyStateTransformer(
|
|||
private val clickIntents: AmountScreenClickIntents,
|
||||
private val swapDirection: SwapDirection,
|
||||
private val isBalanceHidden: Boolean,
|
||||
private val showBestRateAnimation: Boolean,
|
||||
) : Transformer<SwapAmountUM> {
|
||||
|
||||
private val amountFieldConverter = SwapAmountFieldConverter(
|
||||
|
|
@ -51,6 +52,7 @@ internal class SwapAmountSecondaryReadyStateTransformer(
|
|||
swapQuotes = persistentListOf(),
|
||||
selectedQuote = SwapQuoteUM.Empty,
|
||||
appCurrency = appCurrency,
|
||||
showBestRateAnimation = showBestRateAnimation,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ internal class SwapAmountSetQuotesTransformer(
|
|||
val selectedQuote = if (isSilentReload) {
|
||||
prevState.selectedQuote
|
||||
} else {
|
||||
bestQuote
|
||||
(bestQuote as? SwapQuoteUM.Content)?.copy(diffPercent = DifferencePercent.Best) ?: bestQuote
|
||||
}
|
||||
|
||||
val selectQuoteTransformer = SwapAmountSelectQuoteTransformer(
|
||||
|
|
|
|||
|
|
@ -37,8 +37,9 @@ import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
|
|||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
||||
import com.tangem.features.swap.v2.impl.amount.ui.preview.SwapAmountContentPreview
|
||||
import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderContent
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries")
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries", "LongParameterList")
|
||||
@Composable
|
||||
internal fun SwapAmountBlockContent(
|
||||
amountUM: SwapAmountUM,
|
||||
|
|
@ -46,6 +47,7 @@ internal fun SwapAmountBlockContent(
|
|||
onProviderSelectClick: () -> Unit,
|
||||
onInfoClick: () -> Unit,
|
||||
onClick: () -> Unit,
|
||||
onFinishAnimation: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (amountUM !is SwapAmountUM.Content) return
|
||||
|
|
@ -98,9 +100,14 @@ internal fun SwapAmountBlockContent(
|
|||
end.linkTo(parent.end)
|
||||
},
|
||||
)
|
||||
val quoteContent = amountUM.selectedQuote as? SwapQuoteUM.Content
|
||||
val isBestRate = quoteContent?.diffPercent is SwapQuoteUM.Content.DifferencePercent.Best
|
||||
SwapChooseProviderContent(
|
||||
isBestRate = isBestRate,
|
||||
showBestRateAnimation = amountUM.showBestRateAnimation,
|
||||
expressProvider = amountUM.selectedQuote.provider,
|
||||
onClick = onProviderSelectClick,
|
||||
onFinishAnimation = onFinishAnimation,
|
||||
modifier = Modifier.constrainAs(provider) {
|
||||
top.linkTo(to.bottom)
|
||||
bottom.linkTo(parent.bottom)
|
||||
|
|
@ -195,6 +202,7 @@ private fun SwapAmountBlockContent_Preview() {
|
|||
onProviderSelectClick = {},
|
||||
onInfoClick = {},
|
||||
onClick = {},
|
||||
onFinishAnimation = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ internal data object SwapAmountContentPreview {
|
|||
secondaryCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
swapRateType = ExpressRateType.Float,
|
||||
appCurrency = AppCurrency.Default,
|
||||
showBestRateAnimation = false,
|
||||
)
|
||||
|
||||
val defaultState = SwapAmountUM.Content(
|
||||
|
|
@ -124,5 +125,6 @@ internal data object SwapAmountContentPreview {
|
|||
secondaryCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
swapRateType = ExpressRateType.Float,
|
||||
isPrimaryButtonEnabled = true,
|
||||
showBestRateAnimation = false,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.features.swap.v2.impl.chooseprovider.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
|
|
@ -11,6 +14,8 @@ import androidx.compose.material3.Icon
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -21,6 +26,10 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ConstrainedLayoutReference
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.ConstraintLayoutScope
|
||||
import androidx.constraintlayout.compose.Visibility
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
|
|
@ -33,9 +42,17 @@ import com.tangem.domain.express.models.ExpressProvider
|
|||
import com.tangem.domain.express.models.ExpressProviderType
|
||||
import com.tangem.domain.express.models.ExpressRateType
|
||||
import com.tangem.features.swap.v2.impl.R
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
fun SwapChooseProviderContent(expressProvider: ExpressProvider?, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
fun SwapChooseProviderContent(
|
||||
expressProvider: ExpressProvider?,
|
||||
isBestRate: Boolean,
|
||||
showBestRateAnimation: Boolean,
|
||||
onClick: () -> Unit,
|
||||
onFinishAnimation: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
|
|
@ -48,57 +65,193 @@ fun SwapChooseProviderContent(expressProvider: ExpressProvider?, onClick: () ->
|
|||
color = TangemTheme.colors.stroke.primary,
|
||||
modifier = Modifier.padding(horizontal = 12.dp),
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier.padding(12.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(
|
||||
ImageVector.vectorResource(R.drawable.ic_stack_new_24),
|
||||
),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(start = 12.dp, top = 12.dp, bottom = 12.dp),
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.express_provider),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
modifier = Modifier.padding(start = 8.dp, top = 12.dp, bottom = 12.dp),
|
||||
)
|
||||
SpacerWMax()
|
||||
SubcomposeAsyncImage(
|
||||
modifier = modifier
|
||||
.size(20.dp)
|
||||
.clip(RoundedCornerShape(4.dp)),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(expressProvider?.imageLarge)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(false)
|
||||
.build(),
|
||||
loading = { RectangleShimmer(radius = 4.dp) },
|
||||
error = {
|
||||
Box(
|
||||
modifier = Modifier.background(
|
||||
color = TangemColorPalette.Light1,
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
),
|
||||
)
|
||||
ProviderInfo(expressProvider, isBestRate, showBestRateAnimation, onFinishAnimation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProviderInfo(
|
||||
expressProvider: ExpressProvider?,
|
||||
isBestRate: Boolean,
|
||||
showBestRateAnimation: Boolean,
|
||||
onFinishAnimation: () -> Unit,
|
||||
) {
|
||||
ConstraintLayout {
|
||||
val (imageRef, nameRef, iconRef) = createRefs()
|
||||
SubcomposeAsyncImage(
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(expressProvider?.imageLarge)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(false)
|
||||
.build(),
|
||||
loading = { RectangleShimmer(radius = 4.dp) },
|
||||
error = {
|
||||
Box(
|
||||
modifier = Modifier.background(
|
||||
color = TangemColorPalette.Light1,
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
),
|
||||
)
|
||||
},
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.constrainAs(imageRef) {
|
||||
start.linkTo(parent.start)
|
||||
top.linkTo(parent.top)
|
||||
bottom.linkTo(parent.bottom)
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = expressProvider?.name.orEmpty(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
.padding(start = 6.dp)
|
||||
.constrainAs(nameRef) {
|
||||
start.linkTo(imageRef.end)
|
||||
top.linkTo(parent.top)
|
||||
bottom.linkTo(parent.bottom)
|
||||
},
|
||||
)
|
||||
Icon(
|
||||
painter = rememberVectorPainter(
|
||||
ImageVector.vectorResource(R.drawable.ic_chevron_24),
|
||||
),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(start = 4.dp)
|
||||
.constrainAs(iconRef) {
|
||||
start.linkTo(nameRef.end)
|
||||
top.linkTo(parent.top)
|
||||
bottom.linkTo(parent.bottom)
|
||||
end.linkTo(parent.end, 12.dp)
|
||||
},
|
||||
)
|
||||
BestRateBadge(
|
||||
showBestRateAnimation = showBestRateAnimation,
|
||||
isBestRate = isBestRate,
|
||||
ref = imageRef,
|
||||
onFinishAnimation = onFinishAnimation,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber", "LongMethod")
|
||||
@Composable
|
||||
private fun ConstraintLayoutScope.BestRateBadge(
|
||||
showBestRateAnimation: Boolean,
|
||||
isBestRate: Boolean,
|
||||
ref: ConstrainedLayoutReference,
|
||||
onFinishAnimation: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val animateState = remember { MutableTransitionState(false) }
|
||||
|
||||
LaunchedEffect(showBestRateAnimation) {
|
||||
if (showBestRateAnimation) {
|
||||
delay(600L)
|
||||
animateState.targetState = true
|
||||
delay(1_500L)
|
||||
animateState.targetState = false
|
||||
onFinishAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
val iconSize by animateDpAsState(
|
||||
label = "iconSize",
|
||||
targetValue = if (animateState.targetState) {
|
||||
12.dp
|
||||
} else {
|
||||
8.dp
|
||||
},
|
||||
)
|
||||
val iconVerticalPaddings by animateDpAsState(
|
||||
label = "iconVerticalPaddings",
|
||||
targetValue = if (animateState.targetState) {
|
||||
3.dp
|
||||
} else {
|
||||
2.dp
|
||||
},
|
||||
)
|
||||
val iconHorizontalPaddings by animateDpAsState(
|
||||
label = "iconHorizontalPaddings",
|
||||
targetValue = if (animateState.targetState) {
|
||||
4.dp
|
||||
} else {
|
||||
2.dp
|
||||
},
|
||||
)
|
||||
|
||||
val startMargin by animateDpAsState(
|
||||
label = "startMargin",
|
||||
targetValue = if (animateState.targetState) {
|
||||
(-12).dp
|
||||
} else {
|
||||
(-10).dp
|
||||
},
|
||||
)
|
||||
|
||||
val topMargin by animateDpAsState(
|
||||
label = "topMargin",
|
||||
targetValue = if (animateState.targetState) {
|
||||
(-12).dp
|
||||
} else {
|
||||
(-9).dp
|
||||
},
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.constrainAs(createRef()) {
|
||||
start.linkTo(ref.end, startMargin)
|
||||
top.linkTo(ref.bottom, topMargin)
|
||||
visibility = if (isBestRate) Visibility.Visible else Visibility.Gone
|
||||
}
|
||||
.background(TangemTheme.colors.stroke.transparency, RoundedCornerShape(120.dp))
|
||||
.padding(1.5.dp)
|
||||
.background(TangemTheme.colors.icon.accent, RoundedCornerShape(120.dp)),
|
||||
) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(
|
||||
ImageVector.vectorResource(R.drawable.ic_rounded_star_24),
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.constant,
|
||||
modifier = Modifier
|
||||
.padding(iconHorizontalPaddings, iconVerticalPaddings)
|
||||
.size(iconSize),
|
||||
)
|
||||
AnimatedVisibility(
|
||||
visibleState = animateState,
|
||||
enter = expandIn() + fadeIn(),
|
||||
exit = shrinkOut() + fadeOut(),
|
||||
label = "textAnimation",
|
||||
modifier = Modifier.padding(end = 6.dp),
|
||||
) {
|
||||
Text(
|
||||
text = expressProvider?.name.orEmpty(), // todo provider error
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(start = 6.dp),
|
||||
)
|
||||
Icon(
|
||||
painter = rememberVectorPainter(
|
||||
ImageVector.vectorResource(R.drawable.ic_chevron_24),
|
||||
),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
text = stringResourceSafe(R.string.express_provider_best_rate),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -114,6 +267,8 @@ private fun SwapChooseProviderContent_Preview() {
|
|||
modifier = Modifier.background(TangemTheme.colors.background.tertiary),
|
||||
) {
|
||||
SwapChooseProviderContent(
|
||||
isBestRate = true,
|
||||
showBestRateAnimation = true,
|
||||
expressProvider = ExpressProvider(
|
||||
providerId = "changelly",
|
||||
rateTypes = listOf(ExpressRateType.Fixed),
|
||||
|
|
@ -126,6 +281,7 @@ private fun SwapChooseProviderContent_Preview() {
|
|||
slippage = null,
|
||||
),
|
||||
onClick = {},
|
||||
onFinishAnimation = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue