From 25f31f5f4d1790e448ab7a0389c3b3d07898e60c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Jan 2026 13:20:42 +0400 Subject: [PATCH 01/11] Updated on 2026-08-14 --- .../approve/model/YieldSupplyApproveModel.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 88123f909a..cc24881811 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 @@ -23,6 +23,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 @@ -58,6 +59,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 { @@ -157,7 +159,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, From 9d036548f8737eb1933910932726c612fec99e73 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Jan 2026 13:03:21 +0300 Subject: [PATCH 02/11] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index d811278a2d..9f554a7f91 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,7 +5,7 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.33-1398" +tangemBlockchainSdk = "releases-5.33-1406" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "releases-5.33-576" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 032fdee5ec5c6e989c5fdad653d992a55c2b79fa Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Jan 2026 13:27:48 +0300 Subject: [PATCH 03/11] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- tangem-android-tools | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 8285f8d289..b55ca9ec9b 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 8285f8d2896d2994337f55a7b3a54fd5acc24304 +Subproject commit b55ca9ec9bc7ff88a8764f00656ee7a884df9125 diff --git a/tangem-android-tools b/tangem-android-tools index ab6aee1d49..dc9df17919 160000 --- a/tangem-android-tools +++ b/tangem-android-tools @@ -1 +1 @@ -Subproject commit ab6aee1d49ac0cfca644330a525dced725d5a257 +Subproject commit dc9df17919126bce93482559fe26602f9801eb42 From d6f6af79e69c12a48a5002eaa3e49074bef91afd Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Jan 2026 15:55:46 +0300 Subject: [PATCH 04/11] Updated on 2026-08-14 --- .../core/ui/utils/SharedTransitionUtils.kt | 87 ++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) 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 From cea2e103952233348b2050dc1e89b6397a6be26a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Jan 2026 16:26:26 +0100 Subject: [PATCH 05/11] Updated on 2026-08-14 --- .../com/tangem/common/ui/news/ArticleCard.kt | 127 ++++++++--------- .../java/com/tangem/common/ui/news/Tags.kt | 128 ++++++++++++++++++ .../res/drawable/bg_trending_article.webp | Bin 0 -> 23452 bytes .../tangem/features/feed/ui/feed/FeedList.kt | 2 +- 4 files changed, 182 insertions(+), 75 deletions(-) create mode 100644 common/ui/src/main/java/com/tangem/common/ui/news/Tags.kt create mode 100644 core/ui/src/main/res/drawable/bg_trending_article.webp 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/res/drawable/bg_trending_article.webp b/core/ui/src/main/res/drawable/bg_trending_article.webp new file mode 100644 index 0000000000000000000000000000000000000000..42951498d8ff88c566a5e81d56527d863b7d58c7 GIT binary patch literal 23452 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 P%dAQ_jThJe00000uIq=G literal 0 HcmV?d00001 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) { From f8fbb352da47e5a401cadcd7462fcc33124f3ecb Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Jan 2026 18:27:15 +0100 Subject: [PATCH 06/11] Updated on 2026-08-14 --- .../UpdateGlobalFeedStateTransformer.kt | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) 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() { From b4f9d080e529b6b7dc54ac11819a7a4c1c49e456 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Jan 2026 11:30:28 +0400 Subject: [PATCH 07/11] Updated on 2026-08-14 --- .../tangem/feature/swap/model/SwapModel.kt | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) 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 1fab8b4bc9..a007a6697f 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 @@ -219,9 +219,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 && ( @@ -287,7 +295,7 @@ internal class SwapModel @Inject constructor( } modelScope.launch(dispatchers.io) { - if (accountsFeatureToggles.isFeatureEnabled && tangemPayInput == null) { + if (canUseFromAccountCurrencyStatus) { isAccountsMode = isAccountsModeEnabledUseCase.invokeSync() val fromAccountStatus = getAccountCurrencyStatusUseCase.invokeSync( @@ -578,11 +586,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 @@ -1261,7 +1269,7 @@ internal class SwapModel @Inject constructor( fromAccount = foundAccount toToken = initialFromStatus toAccount = if (accountsFeatureToggles.isFeatureEnabled) { - fromAccountCurrencyStatus.account + fromAccountCurrencyStatus?.account } else { null } @@ -1279,7 +1287,7 @@ internal class SwapModel @Inject constructor( } else { fromToken = initialFromStatus fromAccount = if (accountsFeatureToggles.isFeatureEnabled) { - fromAccountCurrencyStatus.account + fromAccountCurrencyStatus?.account } else { null } From e9e6b8bcc616811665646906e14bf8dbeff92f7e Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 2 Feb 2026 12:22:49 +0300 Subject: [PATCH 08/11] Updated on 2026-08-14 --- .../core/ui/components/token/TokenItem.kt | 4 +- .../ui/components/tokenlist/TokenListItem.kt | 83 +++++++++++-------- .../organizetokens/OrganizeTokensScreen.kt | 5 +- .../MultiCurrencyAccountContent.kt | 7 +- 4 files changed, 57 insertions(+), 42 deletions(-) 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/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, ), ) { From a52c3623bd51f8e57310507310b170624d9f532b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 2 Feb 2026 16:25:50 +0700 Subject: [PATCH 09/11] Updated on 2026-08-14 --- .../market/details/portfolio/add/AvailableToAddData.kt | 9 ++++++--- .../add/impl/converter/AvailableToAddDataConverter.kt | 1 - .../portfolio/add/impl/model/AddToPortfolioModel.kt | 5 +++-- .../markets/portfolio/add/api/AvailableToAddData.kt | 10 +++++++--- .../add/impl/converter/AvailableToAddDataConverter.kt | 1 - .../portfolio/add/impl/model/AddToPortfolioModel.kt | 5 +++-- 6 files changed, 19 insertions(+), 12 deletions(-) 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 72b3e4b07b..299f1d72ed 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/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, From 550883cc8847f825937c252ed0c00a57a2021b0c Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 2 Feb 2026 13:46:00 +0400 Subject: [PATCH 10/11] Updated on 2026-08-14 --- .../java/com/tangem/tap/routing/utils/ChildFactory.kt | 4 +--- .../src/main/kotlin/com/tangem/common/routing/AppRoute.kt | 4 +--- .../tangempay/components/TangemPayOnboardingComponent.kt | 4 +--- .../features/tangempay/model/TangemPayOnboardingModel.kt | 8 +------- .../child/wallet/model/intents/TangemPayClickIntents.kt | 2 +- 5 files changed, 5 insertions(+), 17 deletions(-) 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/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)) } From 95ebe65e573899b3f411a3b0f30efec546ba8539 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 2 Feb 2026 13:08:39 +0300 Subject: [PATCH 11/11] Updated on 2026-08-14 --- .../feature/swap/domain/SwapInteractorImpl.kt | 10 ++++------ .../tangem/feature/swap/DefaultSwapComponent.kt | 14 ++++---------- 2 files changed, 8 insertions(+), 16 deletions(-) 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 286471b0a0..217144c02a 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 @@ -1535,9 +1535,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( @@ -2079,14 +2077,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 @@ -2110,7 +2108,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( val derivationPath = fromToken.network.derivationPath.value // 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()) {