diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt index edf8c36cc3..682bf87475 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt @@ -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(), + ) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/swap/DefaultSwapBestRateAnimationStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/swap/DefaultSwapBestRateAnimationStore.kt new file mode 100644 index 0000000000..4f207731c1 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swap/DefaultSwapBestRateAnimationStore.kt @@ -0,0 +1,21 @@ +package com.tangem.datasource.local.swap + +import com.tangem.datasource.local.datastore.RuntimeSharedStore + +internal class DefaultSwapBestRateAnimationStore( + private val dataStore: RuntimeSharedStore, +) : SwapBestRateAnimationStore, RuntimeSharedStore 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 + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/swap/SwapBestRateAnimationStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/swap/SwapBestRateAnimationStore.kt new file mode 100644 index 0000000000..7fc8e8a1d0 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swap/SwapBestRateAnimationStore.kt @@ -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 +} \ No newline at end of file diff --git a/features/swap-v2/impl/build.gradle.kts b/features/swap-v2/impl/build.gradle.kts index c1d107eaf9..5cb929fe7e 100644 --- a/features/swap-v2/impl/build.gradle.kts +++ b/features/swap-v2/impl/build.gradle.kts @@ -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) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt index 5917c12bf3..0d6966910d 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt @@ -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 diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt index fea9701d97..4c077efd90 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt @@ -51,6 +51,7 @@ internal sealed class SwapAmountUM { // extra data val appCurrency: AppCurrency?, + val showBestRateAnimation: Boolean, ) : SwapAmountUM() } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index f1d6be3848..149ccd0de1 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -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 = SlotNavigation() + var showBestRateAnimation: Boolean = false + val uiState: StateFlow 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) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt index 9aeeb9f2d7..f6c07b51ce 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt @@ -23,6 +23,7 @@ internal class SwapAmountPrimaryReadyStateTransformer( private val clickIntents: AmountScreenClickIntents, private val swapDirection: SwapDirection, private val isBalanceHidden: Boolean, + private val showBestRateAnimation: Boolean, ) : Transformer { private val amountFieldConverter = SwapAmountFieldConverter( @@ -52,6 +53,7 @@ internal class SwapAmountPrimaryReadyStateTransformer( swapQuotes = persistentListOf(), selectedQuote = SwapQuoteUM.Empty, appCurrency = appCurrency, + showBestRateAnimation = showBestRateAnimation, ) } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt index 9f54cc94b6..729519512d 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt @@ -24,6 +24,7 @@ internal class SwapAmountSecondaryReadyStateTransformer( private val clickIntents: AmountScreenClickIntents, private val swapDirection: SwapDirection, private val isBalanceHidden: Boolean, + private val showBestRateAnimation: Boolean, ) : Transformer { private val amountFieldConverter = SwapAmountFieldConverter( @@ -51,6 +52,7 @@ internal class SwapAmountSecondaryReadyStateTransformer( swapQuotes = persistentListOf(), selectedQuote = SwapQuoteUM.Empty, appCurrency = appCurrency, + showBestRateAnimation = showBestRateAnimation, ) } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt index 614f1d8b53..665a6a37fb 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt @@ -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( diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt index 291627de98..e2a6522a74 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt @@ -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 = {}, ) } } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt index e7bf485c35..df8d9da742 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt @@ -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, ) } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt index 38a3b4718c..2ef78b6506 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt @@ -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 = {}, ) } }