diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index f182a2555b..cb500685e1 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -691,9 +691,7 @@ internal class ChildFactory @Inject constructor( deeplink = mode.deeplink, ) is AppRoute.TangemPayOnboarding.Mode.FromBannerInSettings -> FromBannerInSettings - is AppRoute.TangemPayOnboarding.Mode.FromBannerOnMain -> FromBannerOnMain( - userWalletId = mode.userWalletId, - ) + is AppRoute.TangemPayOnboarding.Mode.FromBannerOnMain -> FromBannerOnMain }, componentFactory = tangemPayOnboardingComponentFactory, ) diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 2be124d092..4b9bd5496d 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -444,9 +444,7 @@ sealed class AppRoute(val path: String) : Route { ) : Mode() @Serializable - data class FromBannerOnMain( - val userWalletId: UserWalletId, - ) : Mode() + data object FromBannerOnMain : Mode() @Serializable data object FromBannerInSettings : Mode() diff --git a/common/ui/src/main/java/com/tangem/common/ui/news/ArticleCard.kt b/common/ui/src/main/java/com/tangem/common/ui/news/ArticleCard.kt index 43f01a3e93..a6a7a2a078 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/news/ArticleCard.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/news/ArticleCard.kt @@ -2,7 +2,10 @@ package com.tangem.common.ui.news import android.content.res.Configuration import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CardColors import androidx.compose.material3.Text @@ -11,6 +14,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -21,15 +26,12 @@ import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.components.SpacerHMax import com.tangem.core.ui.components.block.BlockCard import com.tangem.core.ui.components.block.TangemBlockCardColors -import com.tangem.core.ui.components.label.Label import com.tangem.core.ui.components.label.entity.LabelUM import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.utils.StringsSigns -import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet @@ -65,52 +67,61 @@ private fun TrendingArticle( colors: CardColors, modifier: Modifier = Modifier, ) { - Column( + Box( modifier = modifier .clip(TangemTheme.shapes.roundedCornersXMedium) .background(colors.containerColor) - .clickable { onArticleClick() } - .padding(vertical = 24.dp, horizontal = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally, + .clickable { onArticleClick() }, ) { - Text( - modifier = Modifier - .background( - color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f), - shape = RoundedCornerShape(8.dp), - ) - .padding(horizontal = 8.dp, vertical = 4.dp), - text = stringResourceSafe(R.string.feed_trending_now), - style = TangemTheme.typography.caption1, - color = TangemTheme.colors.icon.accent, + Image( + modifier = Modifier.matchParentSize(), + painter = painterResource(R.drawable.bg_trending_article), + contentDescription = null, + contentScale = ContentScale.FillWidth, ) + Column( + modifier = Modifier.padding(vertical = 24.dp, horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + modifier = Modifier + .background( + color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f), + shape = RoundedCornerShape(8.dp), + ) + .padding(horizontal = 8.dp, vertical = 4.dp), + text = stringResourceSafe(R.string.feed_trending_now), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.icon.accent, + ) - SpacerH(12.dp) + SpacerH(12.dp) - Text( - text = articleConfigUM.title, - color = if (articleConfigUM.isViewed) { - TangemTheme.colors.text.tertiary - } else { - TangemTheme.colors.text.primary1 - }, - style = TangemTheme.typography.h3, - textAlign = TextAlign.Center, - ) + Text( + text = articleConfigUM.title, + color = if (articleConfigUM.isViewed) { + TangemTheme.colors.text.tertiary + } else { + TangemTheme.colors.text.primary1 + }, + style = TangemTheme.typography.h3, + textAlign = TextAlign.Center, + ) - SpacerH(8.dp) + SpacerH(8.dp) - ArticleInfo( - score = articleConfigUM.score, - createdAt = articleConfigUM.createdAt.resolveReference(), - ) + ArticleInfo( + score = articleConfigUM.score, + createdAt = articleConfigUM.createdAt.resolveReference(), + ) - SpacerH(32.dp) + SpacerH(32.dp) - Tags( - modifier = Modifier.padding(horizontal = 46.dp), - tags = articleConfigUM.tags.toImmutableList(), - ) + Tags( + modifier = Modifier.padding(horizontal = 46.dp), + tags = articleConfigUM.tags.toImmutableList(), + ) + } } } @@ -182,46 +193,14 @@ private fun DefaultArticle( overflow = TextOverflow.Ellipsis, ) - SpacerHMax(modifier = Modifier.padding(bottom = 16.dp)) + SpacerH(16.dp) + + SpacerHMax() Tags(tags = articleConfigUM.tags.toImmutableList()) } } -@Composable -private fun Tags(tags: ImmutableList, modifier: Modifier = Modifier) { - FlowRow( - modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(4.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - maxItemsInEachRow = MAX_TAGS_COUNT_TO_SHOW + 1, - maxLines = 1, - itemVerticalAlignment = Alignment.Bottom, - ) { - val displayedTags = if (tags.size > MAX_TAGS_COUNT_TO_SHOW) { - tags.take(MAX_TAGS_COUNT_TO_SHOW) - } else { - tags - } - - displayedTags.forEach { tag -> - Label(state = tag) - } - - if (tags.size > MAX_TAGS_COUNT_TO_SHOW) { - val remainingItems = tags.size - MAX_TAGS_COUNT_TO_SHOW - Label( - state = LabelUM( - text = TextReference.Str("${StringsSigns.PLUS}$remainingItems"), - maxLines = 1, - ), - ) - } - } -} - -private const val MAX_TAGS_COUNT_TO_SHOW = 3 - @Preview(showBackground = true, widthDp = 360) @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable diff --git a/common/ui/src/main/java/com/tangem/common/ui/news/Tags.kt b/common/ui/src/main/java/com/tangem/common/ui/news/Tags.kt new file mode 100644 index 0000000000..a163c8ee15 --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/news/Tags.kt @@ -0,0 +1,128 @@ +package com.tangem.common.ui.news + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.Placeable +import androidx.compose.ui.layout.SubcomposeLayout +import androidx.compose.ui.layout.SubcomposeMeasureScope +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.label.Label +import com.tangem.core.ui.components.label.entity.LabelUM +import com.tangem.core.ui.extensions.TextReference +import com.tangem.utils.StringsSigns +import kotlinx.collections.immutable.ImmutableList + +private val TAG_SPACING = 4.dp + +/** + * A layout that displays a list of tags, automatically calculating how many can fit + * in a single line. If not all tags fit, it shows an overflow indicator (e.g., "+N"). + * + * @param tags The immutable list of [LabelUM] to display. + * @param modifier The modifier to be applied to the layout. + */ +@Composable +internal fun Tags(tags: ImmutableList, modifier: Modifier = Modifier) { + if (tags.isEmpty()) return + + SubcomposeLayout(modifier = modifier) { constraints -> + val spacingPx = TAG_SPACING.roundToPx() + + val tagPlaceables = subcompose(ContentSlot.Tags) { + tags.forEach { tag -> + Label(state = tag) + } + }.map { it.measure(constraints) } + + val layoutInfo = calculateLayoutInfo( + maxWidth = constraints.maxWidth, + spacingPx = spacingPx, + tagPlaceables = tagPlaceables, + ) + + val overflowPlaceable = layoutInfo.overflowCount.takeIf { it > 0 }?.let { + subcompose(ContentSlot.Overflow) { + OverflowLabel(count = it) + }.first().measure(constraints) + } + + val visiblePlaceables = tagPlaceables.take(layoutInfo.visibleCount) + val allVisibleItems = visiblePlaceables + listOfNotNull(overflowPlaceable) + + val width = calculateRowWidth( + placeables = allVisibleItems, + spacingPx = spacingPx, + ).coerceIn(constraints.minWidth, constraints.maxWidth) + + val height = allVisibleItems.maxOfOrNull { it.height } ?: 0 + + layout(width = width, height = height) { + var xPosition = 0 + allVisibleItems.forEachIndexed { index, placeable -> + if (index > 0) { + xPosition += spacingPx + } + val yPosition = height - placeable.height + placeable.placeRelative(xPosition, yPosition) + xPosition += placeable.width + } + } + } +} + +/** + * Calculates how many tags can be displayed and how many are overflowing. + */ +private fun SubcomposeMeasureScope.calculateLayoutInfo( + maxWidth: Int, + spacingPx: Int, + tagPlaceables: List, +): TagsLayoutInfo { + val totalWidthWithoutOverflow = calculateRowWidth(tagPlaceables, spacingPx) + + if (totalWidthWithoutOverflow <= maxWidth) { + return TagsLayoutInfo(visibleCount = tagPlaceables.size, overflowCount = 0) + } + + for (count in tagPlaceables.indices.reversed()) { + val visibleTags = tagPlaceables.take(count) + val overflowCount = tagPlaceables.size - count + + val tempOverflowPlaceable = subcompose("overflow_temp_$overflowCount") { + OverflowLabel(count = overflowCount) + }.first().measure(Constraints()) + + val widthWithOverflow = calculateRowWidth( + placeables = visibleTags + tempOverflowPlaceable, + spacingPx = spacingPx, + ) + + if (widthWithOverflow <= maxWidth) { + return TagsLayoutInfo(visibleCount = count, overflowCount = overflowCount) + } + } + + return TagsLayoutInfo(visibleCount = 0, overflowCount = tagPlaceables.size) +} + +@Composable +private fun OverflowLabel(count: Int) { + Label( + state = LabelUM( + text = TextReference.Str("${StringsSigns.PLUS}$count"), + maxLines = 1, + ), + ) +} + +private fun calculateRowWidth(placeables: List, spacingPx: Int): Int { + if (placeables.isEmpty()) return 0 + val tagsWidth = placeables.sumOf { it.width } + val spacingWidth = spacingPx * (placeables.size - 1).coerceAtLeast(0) + return tagsWidth + spacingWidth +} + +private data class TagsLayoutInfo(val visibleCount: Int, val overflowCount: Int) + +private enum class ContentSlot { Tags, Overflow } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt index 631d330388..d76f2160fb 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt @@ -120,7 +120,7 @@ fun TokenItem( composables.icon.invoke( Modifier .layoutId(layoutId = LayoutId.ICON) - .padding(end = TangemTheme.dimens.spacing8) + .padding(end = TangemTheme.dimens.spacing12) .testTag(TokenElementsTestTags.TOKEN_ICON), ) } else { @@ -128,7 +128,7 @@ fun TokenItem( state = state.iconState, modifier = Modifier .layoutId(layoutId = LayoutId.ICON) - .padding(end = TangemTheme.dimens.spacing8) + .padding(end = TangemTheme.dimens.spacing12) .testTag(TokenElementsTestTags.TOKEN_ICON), ) } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt index ddfbb831bf..4b016c5544 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt @@ -7,8 +7,10 @@ import androidx.compose.animation.core.tween import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material3.Icon +import androidx.compose.material3.LocalRippleConfiguration import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -32,6 +34,7 @@ import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.components.tokenlist.internal.GroupTitleItem import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM +import com.tangem.core.ui.extensions.orMaskWithStars import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.utils.ProvideSharedTransitionScope @@ -71,13 +74,13 @@ fun PortfolioListItem(state: TokensListItemUM.Portfolio, isBalanceHidden: Boolea ProvideSharedTransitionScope(modifier) { val iconSharedContentState = rememberSharedContentState(key = "icon_${state.id}") val titleSharedContentState = rememberSharedContentState(key = "title_${state.id}") - val boundsTransform = BoundsTransform { _, _ -> tween(350) } + val boundsTransform = BoundsTransform { _, _ -> tween(250) } AnimatedContent( targetState = state.isExpanded, transitionSpec = { - fadeIn(animationSpec = tween(350, delayMillis = 90)) - .togetherWith(fadeOut(animationSpec = tween(350))) + fadeIn(animationSpec = tween(250, delayMillis = 90)) + .togetherWith(fadeOut(animationSpec = tween(250))) }, ) { isExpanded -> val animatedContentScope = this @@ -116,7 +119,7 @@ fun PortfolioListItem(state: TokensListItemUM.Portfolio, isBalanceHidden: Boolea val textSize by animateFloatAsState( targetValue = textStyle.fontSize.value, - animationSpec = tween(350), + animationSpec = tween(250), ) TokenTitle( @@ -133,32 +136,34 @@ fun PortfolioListItem(state: TokensListItemUM.Portfolio, isBalanceHidden: Boolea }, ) - if (isExpanded) { - ExpandedPortfolioHeader( - state = state.tokenItemUM, - isCollapsable = state.isCollapsable, - composables = composables, - balanceTextModifier = Modifier - .animateEnterExit( - enter = fadeIn(tween(350)) + - slideInHorizontally( - tween(350), - initialOffsetX = { fullWidth -> fullWidth * 2 }, - ), - exit = fadeOut( - tween(350), - ) + slideOutHorizontally( - tween(350), - targetOffsetX = { fullWidth -> fullWidth * 2 }, + CompositionLocalProvider(LocalRippleConfiguration provides null) { + if (isExpanded) { + ExpandedPortfolioHeader( + state = state.tokenItemUM, + isCollapsable = state.isCollapsable, + isBalanceHidden = isBalanceHidden, + composables = composables, + balanceTextModifier = Modifier + .animateEnterExit( + enter = fadeIn(tween(250)) + + slideInHorizontally( + tween(250), + initialOffsetX = { fullWidth -> fullWidth }, + ), + exit = fadeOut(tween(250)) + + slideOutHorizontally( + tween(250), + targetOffsetX = { fullWidth -> fullWidth }, + ), ), - ), - ) - } else { - TokenItem( - state = state.tokenItemUM, - isBalanceHidden = isBalanceHidden, - composables = composables, - ) + ) + } else { + TokenItem( + state = state.tokenItemUM, + isBalanceHidden = isBalanceHidden, + composables = composables, + ) + } } } } @@ -180,6 +185,7 @@ fun PortfolioTokensListItem(state: PortfolioTokensListItemUM, isBalanceHidden: B @Composable fun ExpandedPortfolioHeader( state: TokenItemState, + isBalanceHidden: Boolean, isCollapsable: Boolean, composables: TokenItemComposables?, modifier: Modifier = Modifier, @@ -191,8 +197,10 @@ fun ExpandedPortfolioHeader( .fillMaxWidth() .clickable(onClick = { state.onItemClick?.invoke(state) }) .padding( - vertical = if (composables != null) 8.dp else 4.dp, - horizontal = 12.dp, + top = 12.dp, + bottom = 4.dp, + start = 12.dp, + end = 12.dp, ), ) { if (composables != null) { @@ -227,12 +235,19 @@ fun ExpandedPortfolioHeader( modifier = Modifier.weight(1f), ) { if (composables != null) { - composables.title.invoke(Modifier.alignByBaseline()) + composables.title.invoke( + Modifier + .padding(vertical = 2.dp) + .alignByBaseline(), + ) } else { when (val titleState = state.titleState) { TokenItemState.TitleState.Loading -> Unit TokenItemState.TitleState.Locked -> Unit is TokenItemState.TitleState.Content -> Text( + modifier = Modifier + .padding(vertical = 2.dp) + .alignByBaseline(), text = titleState.text.resolveReference(), color = TangemTheme.colors.text.primary1, overflow = TextOverflow.Ellipsis, @@ -249,10 +264,10 @@ fun ExpandedPortfolioHeader( } Text( - text = text.orEmpty(), + text = text.orEmpty().orMaskWithStars(isBalanceHidden), modifier = balanceTextModifier .alignByBaseline() - .padding(horizontal = 8.dp), + .padding(horizontal = 4.dp), color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.caption1, overflow = TextOverflow.Ellipsis, diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/SharedTransitionUtils.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/SharedTransitionUtils.kt index 4851e2e34f..801c42088c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/SharedTransitionUtils.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/SharedTransitionUtils.kt @@ -1,5 +1,9 @@ package com.tangem.core.ui.utils +import androidx.compose.animation.AnimatedVisibilityScope +import androidx.compose.animation.BoundsTransform +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition import androidx.compose.animation.SharedTransitionLayout import androidx.compose.animation.SharedTransitionScope import androidx.compose.foundation.layout.Box @@ -7,6 +11,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.layout.LayoutCoordinates +import androidx.compose.ui.layout.Placeable +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.LayoutDirection @Composable fun TangemSharedTransitionLayout( @@ -31,6 +42,78 @@ fun ProvideSharedTransitionScope(modifier: Modifier = Modifier, content: @Compos } } -private val LocalSharedTransitionScope = staticCompositionLocalOf { - error("No SharedTransitionScope provided") +private val LocalSharedTransitionScope = staticCompositionLocalOf { StubSharedTransitionScope() } + +private class StubSharedTransitionScope : SharedTransitionScope { + override val isTransitionActive: Boolean = false + + override val Placeable.PlacementScope.lookaheadScopeCoordinates: LayoutCoordinates + get() = error("No LookaheadScopeCoordinates available in StubSharedTransitionScope") + + override fun Modifier.skipToLookaheadSize(enabled: () -> Boolean): Modifier { + return this + } + + override fun Modifier.renderInSharedTransitionScopeOverlay( + zIndexInOverlay: Float, + renderInOverlay: () -> Boolean, + ): Modifier { + return this + } + + override fun Modifier.sharedElement( + sharedContentState: SharedTransitionScope.SharedContentState, + animatedVisibilityScope: AnimatedVisibilityScope, + boundsTransform: BoundsTransform, + placeholderSize: SharedTransitionScope.PlaceholderSize, + renderInOverlayDuringTransition: Boolean, + zIndexInOverlay: Float, + clipInOverlayDuringTransition: SharedTransitionScope.OverlayClip, + ): Modifier { + return this + } + + override fun Modifier.sharedBounds( + sharedContentState: SharedTransitionScope.SharedContentState, + animatedVisibilityScope: AnimatedVisibilityScope, + enter: EnterTransition, + exit: ExitTransition, + boundsTransform: BoundsTransform, + resizeMode: SharedTransitionScope.ResizeMode, + placeholderSize: SharedTransitionScope.PlaceholderSize, + renderInOverlayDuringTransition: Boolean, + zIndexInOverlay: Float, + clipInOverlayDuringTransition: SharedTransitionScope.OverlayClip, + ): Modifier { + return this + } + + override fun Modifier.sharedElementWithCallerManagedVisibility( + sharedContentState: SharedTransitionScope.SharedContentState, + visible: Boolean, + boundsTransform: BoundsTransform, + placeholderSize: SharedTransitionScope.PlaceholderSize, + renderInOverlayDuringTransition: Boolean, + zIndexInOverlay: Float, + clipInOverlayDuringTransition: SharedTransitionScope.OverlayClip, + ): Modifier { + return this + } + + override fun OverlayClip(clipShape: Shape): SharedTransitionScope.OverlayClip { + return object : SharedTransitionScope.OverlayClip { + override fun getClipPath( + sharedContentState: SharedTransitionScope.SharedContentState, + bounds: Rect, + layoutDirection: LayoutDirection, + density: Density, + ): Path? { + return Path() + } + } + } + + override fun LayoutCoordinates.toLookaheadCoordinates(): LayoutCoordinates { + return this + } } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/bg_trending_article.webp b/core/ui/src/main/res/drawable/bg_trending_article.webp new file mode 100644 index 0000000000..42951498d8 Binary files /dev/null and b/core/ui/src/main/res/drawable/bg_trending_article.webp differ diff --git a/features/feed/api/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/AvailableToAddData.kt b/features/feed/api/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/AvailableToAddData.kt index 9d221287dd..b868b9b8be 100644 --- a/features/feed/api/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/AvailableToAddData.kt +++ b/features/feed/api/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/AvailableToAddData.kt @@ -12,8 +12,7 @@ import kotlinx.serialization.Serializable data class AvailableToAddData( val availableToAddWallets: Map, ) { - val isAvailableToAdd: Boolean - get() = availableToAddWallets.isNotEmpty() + val isAvailableToAdd: Boolean = availableToAddWallets.values.any { item -> item.isAvailableToAdd } val isSinglePortfolio: Boolean get() = availableToAddWallets.size == 1 && availableToAddWallets.values.first().accounts.size == 1 } @@ -23,7 +22,9 @@ data class AvailableToAddWallet( val accounts: List, val availableNetworks: Set, val availableToAddAccounts: Map, -) +) { + val isAvailableToAdd: Boolean = availableToAddAccounts.values.any { item -> item.isAvailableToAdd } +} @Serializable data class AvailableToAddAccount( @@ -38,6 +39,8 @@ data class AvailableToAddAccount( .filter { available -> addedNetworks.none { added -> added.backendId == available.networkId } } .toSet() + val isAvailableToAdd: Boolean = availableToAddNetworks.isNotEmpty() + val addedMarketNetworks: Set = availableNetworks .filter { available -> addedNetworks.any { added -> added.backendId == available.networkId } } .toSet() diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/converter/AvailableToAddDataConverter.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/converter/AvailableToAddDataConverter.kt index 49d3820aa2..a6b02044fb 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/converter/AvailableToAddDataConverter.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/converter/AvailableToAddDataConverter.kt @@ -70,7 +70,6 @@ internal class AvailableToAddDataConverter @Inject constructor( val availableToAddAccount = accountStatus.getAvailableToAddAccount(wallet) ?: return@mapNotNull null accountStatus.account.accountId to availableToAddAccount } - .filter { (_, account) -> account.availableToAddNetworks.isNotEmpty() } .toMap() return AvailableToAddWallet( userWallet = wallet, diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioModel.kt index b31e57f8b0..a06d46878d 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioModel.kt @@ -344,9 +344,10 @@ internal class AddToPortfolioModel @Inject constructor( portfolioSelectorController.isEnabled.value = isEnabled@{ userWallet, accountStatus -> val availableWallet = state.availableToAddData.availableToAddWallets[userWallet.walletId] ?: return@isEnabled false - val availableAccount = + val isAvailableAccount = availableWallet.availableToAddAccounts[accountStatus.account.accountId] - return@isEnabled availableAccount != null + ?.isAvailableToAdd == true + return@isEnabled isAvailableAccount } AddToPortfolioManager.State.Init, AddToPortfolioManager.State.NothingToAdd, diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/state/transformers/UpdateGlobalFeedStateTransformer.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/state/transformers/UpdateGlobalFeedStateTransformer.kt index d6e5ec6be2..dc78f2b7a0 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/state/transformers/UpdateGlobalFeedStateTransformer.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/state/transformers/UpdateGlobalFeedStateTransformer.kt @@ -25,15 +25,19 @@ internal class UpdateGlobalFeedStateTransformer( prevState.news.newsUMState == NewsUMState.ERROR val hasLoadingInCharts = loadingStatesByOrder.values.any { it } - val hasErrorInCharts = errorStatesByOrder.values.any { it != null } - val hasAllErrorInCharts = errorStatesByOrder.values.isNotEmpty() && errorStatesByOrder.values.all { it != null } + val hasErrorInMarketChart = errorStatesByOrder[SortByTypeUM.Rating] != null + val hasErrorInMarketPulseChart = errorStatesByOrder + .any { it.key != SortByTypeUM.Rating && it.value != null } val areAllChartsLoading = loadingStatesByOrder.values.isNotEmpty() && loadingStatesByOrder.values.all { it } - val areAllChartsError = hasAllErrorInCharts || hasErrorInCharts && hasLoadingInCharts + val areAllChartsError = when { + hasErrorInMarketChart && hasErrorInMarketPulseChart -> true + hasErrorInMarketPulseChart && hasLoadingInCharts -> true + hasErrorInMarketChart && hasLoadingInCharts -> true + else -> false + } val newGlobalState = when { - isNewsLoading && areAllChartsError -> GlobalFeedState.Loading - isNewsLoading && areAllChartsLoading -> GlobalFeedState.Loading isNewsError && areAllChartsLoading -> GlobalFeedState.Loading isNewsError && areAllChartsError -> { if (previousGlobalState !is GlobalFeedState.Error) { @@ -43,18 +47,11 @@ internal class UpdateGlobalFeedStateTransformer( onRetryClicked = onRetryClicked, ) } + isNewsLoading && areAllChartsError -> GlobalFeedState.Loading + isNewsLoading && areAllChartsLoading -> GlobalFeedState.Loading else -> GlobalFeedState.Content } - - return prevState.copy( - globalState = newGlobalState, - news = prevState.news.copy( - newsUMState = when { - isNewsError -> NewsUMState.ERROR - else -> NewsUMState.CONTENT - }, - ), - ) + return prevState.copy(globalState = newGlobalState) } private fun sendErrorAnalytics() { diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/FeedList.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/FeedList.kt index 88ebc8dc1f..43dcd4a50e 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/FeedList.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/FeedList.kt @@ -423,7 +423,7 @@ private fun Charts( ) { BlockCard( modifier = modifier, - colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action), + colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.primary), ) { Column(modifier = Modifier.fillMaxWidth()) { when (marketChart) { diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/api/AvailableToAddData.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/api/AvailableToAddData.kt index 3ca633edf5..976ad0a6c4 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/api/AvailableToAddData.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/api/AvailableToAddData.kt @@ -12,8 +12,7 @@ import kotlinx.serialization.Serializable internal data class AvailableToAddData( val availableToAddWallets: Map, ) { - val isAvailableToAdd: Boolean - get() = availableToAddWallets.isNotEmpty() + val isAvailableToAdd: Boolean = availableToAddWallets.values.any { item -> item.isAvailableToAdd } val isSinglePortfolio: Boolean get() = availableToAddWallets.size == 1 && availableToAddWallets.values.first().accounts.size == 1 } @@ -23,7 +22,9 @@ internal data class AvailableToAddWallet( val accounts: List, val availableNetworks: Set, val availableToAddAccounts: Map, -) +) { + val isAvailableToAdd: Boolean = availableToAddAccounts.values.any { item -> item.isAvailableToAdd } +} @Serializable internal data class AvailableToAddAccount( @@ -31,6 +32,7 @@ internal data class AvailableToAddAccount( val availableNetworks: Set, val addedNetworks: Set, ) { + val isSingleNetwork: Boolean get() = availableNetworks.size == 1 @@ -38,6 +40,8 @@ internal data class AvailableToAddAccount( .filter { available -> addedNetworks.none { added -> added.backendId == available.networkId } } .toSet() + val isAvailableToAdd: Boolean = availableToAddNetworks.isNotEmpty() + val addedMarketNetworks: Set = availableNetworks .filter { available -> addedNetworks.any { added -> added.backendId == available.networkId } } .toSet() diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/converter/AvailableToAddDataConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/converter/AvailableToAddDataConverter.kt index a9a11d6eb7..3a311b6854 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/converter/AvailableToAddDataConverter.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/converter/AvailableToAddDataConverter.kt @@ -70,7 +70,6 @@ internal class AvailableToAddDataConverter @Inject constructor( val availableToAddAccount = accountStatus.getAvailableToAddAccount(wallet) ?: return@mapNotNull null accountStatus.account.accountId to availableToAddAccount } - .filter { (_, account) -> account.availableToAddNetworks.isNotEmpty() } .toMap() return AvailableToAddWallet( userWallet = wallet, diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/AddToPortfolioModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/AddToPortfolioModel.kt index b0198b5975..6b0fd6a169 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/AddToPortfolioModel.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/AddToPortfolioModel.kt @@ -334,9 +334,10 @@ internal class AddToPortfolioModel @Inject constructor( portfolioSelectorController.isEnabled.value = isEnabled@{ userWallet, accountStatus -> val availableWallet = state.availableToAddData.availableToAddWallets[userWallet.walletId] ?: return@isEnabled false - val availableAccount = + val isAvailableAccount = availableWallet.availableToAddAccounts[accountStatus.account.accountId] - return@isEnabled availableAccount != null + ?.isAvailableToAdd == true + return@isEnabled isAvailableAccount } AddToPortfolioManager.State.Init, AddToPortfolioManager.State.NothingToAdd, diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 4c063c8564..a8b55e5af2 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -1536,9 +1536,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( swapAmount = amount, quotesLoadedState = swapState, isAllowedToSpend = isAllowedToSpend, - spenderAddress = requireNotNull(quoteModel.allowanceContract) { - "allowanceContract is required for DEX" - }, + spenderAddress = quoteModel.allowanceContract, ) if (state !is SwapState.QuotesLoadedState) return state state.copy( @@ -2081,14 +2079,14 @@ internal class SwapInteractorImpl @AssistedInject constructor( ) ?: TxFeeState.Empty } - @Suppress("LongParameterList", "LongMethod") + @Suppress("LongParameterList", "LongMethod", "CanBeNonNullable") private suspend fun updatePermissionState( networkId: String, fromTokenStatus: CryptoCurrencyStatus, fromAccount: Account.CryptoPortfolio?, swapAmount: SwapAmount, quotesLoadedState: SwapState.QuotesLoadedState, - spenderAddress: String, + spenderAddress: String?, isAllowedToSpend: Boolean, ): SwapState { val fromToken = fromTokenStatus.currency @@ -2111,7 +2109,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( } // setting up amount for approve with given amount for swap [SwapApproveType.Limited] val callData = SmartContractCallDataProviderFactory.getApprovalCallData( - spenderAddress = spenderAddress, + spenderAddress = requireNotNull(spenderAddress) { "spenderAddress cant be null" }, amount = swapAmount.value.convertToSdkAmount(fromTokenStatus), blockchain = fromToken.network.toBlockchain(), ) diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapComponent.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapComponent.kt index 55e17a1292..6e9495b238 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapComponent.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/DefaultSwapComponent.kt @@ -2,9 +2,7 @@ package com.tangem.feature.swap import androidx.compose.animation.Crossfade import androidx.compose.foundation.background -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.arkivanov.decompose.ComponentContext @@ -103,13 +101,9 @@ internal class DefaultSwapComponent @AssistedInject constructor( @Composable override fun Content(modifier: Modifier) { if (sendFeatureToggles.isGaslessTransactionsEnabled) { - val fromCryptoCurrency = if (model.isOrderReversed) { - model.dataState.toCryptoCurrency - } else { - model.dataState.fromCryptoCurrency - } - val feePaidCryptoCurrency = model.dataState.feePaidCryptoCurrency - val amount = model.dataState.amount + val fromCryptoCurrency by remember { derivedStateOf { model.dataState.fromCryptoCurrency } } + val feePaidCryptoCurrency by remember { derivedStateOf { model.dataState.feePaidCryptoCurrency } } + val amount by remember { derivedStateOf { model.dataState.amount } } LaunchedEffect(fromCryptoCurrency, feePaidCryptoCurrency, amount.isNullOrBlank()) { if (amount.isNullOrBlank()) { diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt index 6ae4815817..8f5bd07b10 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt @@ -220,9 +220,17 @@ internal class SwapModel @Inject constructor( private val swapRouter: SwapRouter = SwapRouter(router = router) private var userCountry: UserCountry? = null - private lateinit var fromAccountCurrencyStatus: AccountCryptoCurrencyStatus + private var fromAccountCurrencyStatus: AccountCryptoCurrencyStatus? = null private var toAccountCurrencyStatus: AccountCryptoCurrencyStatus? = null + /** + * If accountsFeatureToggles is off OR user came from Tangem Pay -> fromAccountCurrencyStatus == null + * If accountsFeatureToggles is on AND user didn't come from Tangem Pay -> fromAccountCurrencyStatus != null + * + * Remove when accounts are integrated into Tangem Pay + */ + private val canUseFromAccountCurrencyStatus = accountsFeatureToggles.isFeatureEnabled && tangemPayInput == null + private val isUserResolvableError: (SwapState) -> Boolean = { swapState -> swapState is SwapState.SwapError && ( @@ -288,7 +296,7 @@ internal class SwapModel @Inject constructor( } modelScope.launch(dispatchers.io) { - if (accountsFeatureToggles.isFeatureEnabled && tangemPayInput == null) { + if (canUseFromAccountCurrencyStatus) { isAccountsMode = isAccountsModeEnabledUseCase.invokeSync() val fromAccountStatus = getAccountCurrencyStatusUseCase.invokeSync( @@ -579,11 +587,11 @@ internal class SwapModel @Inject constructor( } else { initialFromStatus to selectedCurrency } - val (fromAccount, toAccount) = if (accountsFeatureToggles.isFeatureEnabled && tangemPayInput == null) { + val (fromAccount, toAccount) = if (canUseFromAccountCurrencyStatus) { if (isOrderReversed) { - selectedAccount to fromAccountCurrencyStatus.account + selectedAccount to requireNotNull(fromAccountCurrencyStatus).account } else { - fromAccountCurrencyStatus.account to selectedAccount + requireNotNull(fromAccountCurrencyStatus).account to selectedAccount } } else { null to null @@ -1266,7 +1274,7 @@ internal class SwapModel @Inject constructor( fromAccount = foundAccount toToken = initialFromStatus toAccount = if (accountsFeatureToggles.isFeatureEnabled) { - fromAccountCurrencyStatus.account + fromAccountCurrencyStatus?.account } else { null } @@ -1284,7 +1292,7 @@ internal class SwapModel @Inject constructor( } else { fromToken = initialFromStatus fromAccount = if (accountsFeatureToggles.isFeatureEnabled) { - fromAccountCurrencyStatus.account + fromAccountCurrencyStatus?.account } else { null } diff --git a/features/tangempay/onboarding/api/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayOnboardingComponent.kt b/features/tangempay/onboarding/api/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayOnboardingComponent.kt index df83da98da..e8c5c6d08d 100644 --- a/features/tangempay/onboarding/api/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayOnboardingComponent.kt +++ b/features/tangempay/onboarding/api/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayOnboardingComponent.kt @@ -16,9 +16,7 @@ interface TangemPayOnboardingComponent : ComposableContentComponent { val userWalletId: UserWalletId, ) : Params() - data class FromBannerOnMain( - val userWalletId: UserWalletId, - ) : Params() + data object FromBannerOnMain : Params() data object FromBannerInSettings : Params() } diff --git a/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt b/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt index a3dc4ea532..b8ce2a6fac 100644 --- a/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt +++ b/features/tangempay/onboarding/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayOnboardingModel.kt @@ -131,13 +131,7 @@ internal class TangemPayOnboardingModel @Inject constructor( is TangemPayOnboardingComponent.Params.ContinueOnboarding -> { checkCustomerInfo(params.userWalletId) } - is TangemPayOnboardingComponent.Params.FromBannerOnMain -> { - if (eligibleWalletsIds.any { walletId -> walletId == params.userWalletId }) { - checkCustomerInfo(userWalletId = params.userWalletId) - } else { - openWalletSelectorIfNeeds(eligibleWalletsIds) - } - } + is TangemPayOnboardingComponent.Params.FromBannerOnMain, is TangemPayOnboardingComponent.Params.Deeplink, is TangemPayOnboardingComponent.Params.FromBannerInSettings, -> { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/TangemPayClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/TangemPayClickIntents.kt index 57040c28d4..1318303b1a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/TangemPayClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/TangemPayClickIntents.kt @@ -243,7 +243,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor( modelScope.launch { val isEligible = tangemPayEligibilityManager.getTangemPayAvailability() if (isEligible) { - router.openTangemPayOnboarding(mode = AppRoute.TangemPayOnboarding.Mode.FromBannerOnMain(userWalletId)) + router.openTangemPayOnboarding(mode = AppRoute.TangemPayOnboarding.Mode.FromBannerOnMain) } else { stateHolder.update(transformer = TangemPayHideOnboardingStateTransformer(userWalletId)) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt index 08a1123ea0..2996d749be 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt @@ -219,9 +219,8 @@ private fun LazyItemScope.DraggableItem( state = item.tokenItemState, isCollapsable = false, composables = null, - modifier = modifierWithBackground - .padding(top = 8.dp) - .fillMaxWidth(), + isBalanceHidden = isBalanceHidden, + modifier = modifierWithBackground.fillMaxWidth(), ) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAccountContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAccountContent.kt index 66c94df727..1ddf7dd2cf 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAccountContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAccountContent.kt @@ -1,6 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency import androidx.compose.animation.* +import androidx.compose.animation.core.FastOutLinearInEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListScope @@ -141,7 +142,7 @@ private fun LazyListScope.portfolioItem( lastIndexProxy = lastIndex return@LaunchedEffect } - delay(timeMillis = minOf(50 * tokens.size, 350).toLong()) + delay(timeMillis = minOf(50 * tokens.size, 250).toLong()) lastIndexProxy = 0 } @@ -179,13 +180,13 @@ private fun SlideInItemVisibility( enter = fadeIn( tween(100, delayMillis = delayEnter), ) + expandVertically( - tween(100, delayMillis = delayEnter), + tween(100, delayMillis = delayEnter, easing = FastOutLinearInEasing), expandFrom = Alignment.Top, ), exit = fadeOut( tween(100, delayMillis = delayExit), ) + shrinkVertically( - tween(100, delayMillis = delayExit), + tween(100, delayMillis = delayExit, easing = FastOutLinearInEasing), shrinkTowards = Alignment.Top, ), ) { diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt index ca94e2df0c..4ac6a9f1ce 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt @@ -24,6 +24,7 @@ import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyGetContractAddressUseCase +import com.tangem.domain.yield.supply.usecase.YieldSupplyPendingTracker import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics import com.tangem.features.yield.supply.impl.R import com.tangem.features.yield.supply.impl.common.YieldSupplyAlertFactory @@ -59,6 +60,7 @@ internal class YieldSupplyApproveModel @Inject constructor( private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase, private val yieldSupplyGetContractAddressUseCase: YieldSupplyGetContractAddressUseCase, + private val yieldSupplyPendingTracker: YieldSupplyPendingTracker, private val yieldSupplyAlertFactory: YieldSupplyAlertFactory, ) : Model(), YieldSupplyNotificationsComponent.ModelCallback { @@ -159,7 +161,12 @@ internal class YieldSupplyApproveModel @Inject constructor( ) params.callback.onTransactionProgress(false) }, - ifRight = { + ifRight = { txHash -> + yieldSupplyPendingTracker.addPending( + userWalletId = userWallet.walletId, + cryptoCurrency = cryptoCurrency, + txIds = listOf(txHash), + ) val event = AnalyticsParam.TxSentFrom.Earning( blockchain = cryptoCurrency.network.name, token = cryptoCurrency.symbol,