Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-15 08:15:09 +02:00
parent aba2f43c34
commit de3e88227a
10 changed files with 91 additions and 4 deletions

View file

@ -23,6 +23,7 @@ dependencies {
api(projects.features.forYou.api)
api(projects.features.promoBanners.api)
api(projects.features.tokenRecieve.api)
api(projects.features.marketing.api)
/* Data */
implementation(projects.data.common)
@ -49,6 +50,7 @@ dependencies {
implementation(projects.domain.core)
implementation(projects.domain.feedback.models)
implementation(projects.domain.tokens.models)
implementation(projects.domain.marketing.models)
implementation(projects.domain.yieldSupply.models)
runtimeOnly(projects.domain.manageTokens)
runtimeOnly(projects.domain.offramp)

View file

@ -24,6 +24,7 @@ import com.tangem.features.feed.components.search.DefaultSearchComponent
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
import com.tangem.features.foryou.ForYouComponent
import com.tangem.features.foryou.TokenSummaryComponent
import com.tangem.features.marketing.api.MarketingBannerComponent
import com.tangem.features.promobanners.api.PromoBannersBlockComponent
import kotlinx.serialization.Serializable
import javax.inject.Inject
@ -39,6 +40,7 @@ internal class FeedEntryChildFactory @Inject constructor(
private val designFeatureToggles: DesignFeatureToggles,
private val forYouComponentFactory: ForYouComponent.Factory,
private val tokenSummaryComponentFactory: TokenSummaryComponent.Factory,
private val marketingBannerComponentFactory: MarketingBannerComponent.Factory,
) {
@Serializable
@ -103,6 +105,7 @@ internal class FeedEntryChildFactory @Inject constructor(
designFeatureToggles = designFeatureToggles,
addToPortfolioComponentFactory = addToPortfolioComponentFactory,
manageFundsComponentFactory = manageFundsComponentFactory,
marketingBannerComponentFactory = marketingBannerComponentFactory,
)
}
is Child.TokenList -> {

View file

@ -37,6 +37,7 @@ import com.tangem.features.feed.model.market.details.analytics.MarketDetailsAnal
import com.tangem.features.feed.model.market.details.state.TokenNetworksState
import com.tangem.features.feed.ui.market.detailed.MarketsTokenDetailsContent
import com.tangem.features.feed.ui.market.detailed.MarketsTokenDetailsTitle
import com.tangem.features.marketing.api.MarketingBannerComponent
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
@ -51,6 +52,7 @@ internal class DefaultMarketsTokenDetailsComponent(
val params: Params,
private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
private val manageFundsComponentFactory: ManageFundsComponent.Factory,
private val marketingBannerComponentFactory: MarketingBannerComponent.Factory,
) : ComposableModularBottomSheetContentComponent, AppComponentContext by appComponentContext {
// applying l2 compatibility
@ -62,6 +64,11 @@ internal class DefaultMarketsTokenDetailsComponent(
private val analyticsParams = params.analyticsParams
private val model: MarketsTokenDetailsModel = getOrCreateModel(updatedParams)
private val marketingBannerComponent = marketingBannerComponentFactory.create(
context = child("marketsTokenDetailsMarketingBanner"),
params = MarketingBannerComponent.Params.Standalone(requestFlow = model.marketingRequest),
)
private val portfolioComponent: MarketsPortfolioComponent? =
if (updatedParams.shouldShowPortfolio && !designFeatureToggles.isRedesignEnabled) {
portfolioComponentFactory.create(
@ -214,6 +221,9 @@ internal class DefaultMarketsTokenDetailsComponent(
component.Content(blockModifier)
}
},
marketingBanner = { blockModifier ->
marketingBannerComponent.Content(blockModifier)
},
)
bottomSheet.child?.instance?.BottomSheet()
addFundsBs.child?.instance?.BottomSheet()

View file

@ -40,6 +40,7 @@ import com.tangem.domain.card.common.extensions.hotWalletExcludedBlockchains
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.markets.*
import com.tangem.domain.marketing.models.MarketingScreen
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.news.model.NewsListConfig
import com.tangem.domain.news.usecase.GetNewsUseCase
@ -49,6 +50,7 @@ import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.commonfeatures.api.tokenactions.BottomAction
import com.tangem.features.marketing.api.MarketingBannerRequest
import com.tangem.features.feed.components.market.details.AddFundsSlotRoute
import com.tangem.features.feed.components.market.details.AddToPortfolioSlotRoute
import com.tangem.features.feed.components.market.details.DefaultMarketsTokenDetailsComponent
@ -234,6 +236,10 @@ internal class MarketsTokenDetailsModel @Inject constructor(
val isVisibleOnScreen = MutableStateFlow(false)
val networksState = MutableStateFlow<TokenNetworksState>(TokenNetworksState.Loading)
val marketingRequest: Flow<MarketingBannerRequest?> = flowOf(
MarketingBannerRequest(screen = MarketingScreen.TokenMarkets(coingeckoId = params.token.id.value)),
)
val addToPortfolioSheetNavigation = SlotNavigation<AddToPortfolioSlotRoute>()
val addFundsSheetNavigation = SlotNavigation<AddFundsSlotRoute>()

View file

@ -64,6 +64,7 @@ internal fun MarketsTokenDetailsContent(
modifier: Modifier = Modifier,
portfolioBlock: @Composable ((Modifier) -> Unit)?,
portfolioFloatingBlock: @Composable ((Modifier) -> Unit)?,
marketingBanner: @Composable (Modifier) -> Unit,
) {
Content(
contentPadding = contentPadding,
@ -72,6 +73,7 @@ internal fun MarketsTokenDetailsContent(
state = state,
portfolioBlock = portfolioBlock,
portfolioFloatingBlock = portfolioFloatingBlock,
marketingBanner = marketingBanner,
)
when (state.bottomSheetConfig.content) {
@ -90,6 +92,7 @@ private fun Content(
modifier: Modifier = Modifier,
portfolioBlock: @Composable ((Modifier) -> Unit)?,
portfolioFloatingBlock: @Composable ((Modifier) -> Unit)?,
marketingBanner: @Composable (Modifier) -> Unit,
) {
val isRedesignEnabled = LocalRedesignEnabled.current
val density = LocalDensity.current
@ -148,14 +151,13 @@ private fun Content(
)
}
item { SpacerH16() }
tokenMarketDetailsBody(
state = state.body,
portfolioBlock = portfolioBlock,
relatedNews = state.relatedNews,
isRedesignEnabled = isRedesignEnabled,
marketingBanner = marketingBanner,
)
item { SpacerH(bottomSpacing) }
}
}
@ -493,6 +495,7 @@ private fun MarketsTokenDetailsContent_Preview(
backgroundColor = TangemTheme.colors.background.tertiary,
portfolioBlock = {},
portfolioFloatingBlock = null,
marketingBanner = {},
contentPadding = PaddingValues(),
)
}

View file

@ -28,11 +28,13 @@ internal fun LazyListScope.tokenMarketDetailsBody(
state: MarketsTokenDetailsUM.Body,
portfolioBlock: @Composable ((Modifier) -> Unit)?,
relatedNews: RelatedNews,
marketingBanner: @Composable (Modifier) -> Unit,
) {
if (isRedesignEnabled) {
tokenMarketDetailsBodyV2(
state = state,
relatedNews = relatedNews,
marketingBanner = marketingBanner,
)
} else {
tokenMarketDetailsBodyV1(
@ -95,13 +97,28 @@ private fun LazyListScope.tokenMarketDetailsBodyV1(
}
}
private fun LazyListScope.marketingBannerItem(marketingBanner: @Composable (Modifier) -> Unit) {
item(key = "marketing_banner") {
marketingBanner(
Modifier
.padding(horizontal = 16.dp)
.padding(bottom = 8.dp)
.fillMaxWidth(),
)
}
}
// Empty item with a key so that deeplink scroll-to-section can target it before the real content is composed
private fun LazyListScope.sectionStub(key: String) {
item(key) { }
}
@Suppress("CanBeNonNullable")
private fun LazyListScope.tokenMarketDetailsBodyV2(state: MarketsTokenDetailsUM.Body, relatedNews: RelatedNews) {
private fun LazyListScope.tokenMarketDetailsBodyV2(
state: MarketsTokenDetailsUM.Body,
relatedNews: RelatedNews,
marketingBanner: @Composable (Modifier) -> Unit,
) {
when (state) {
MarketsTokenDetailsUM.Body.Loading -> {
item("description-loading") {
@ -115,6 +132,8 @@ private fun LazyListScope.tokenMarketDetailsBodyV2(state: MarketsTokenDetailsUM.
description(state.description)
}
marketingBannerItem(marketingBanner)
infoBlocksListV2(
state = state.infoBlocks,
relatedNews = relatedNews,
@ -169,7 +188,7 @@ private fun LazyListScope.description(description: MarketsTokenDetailsUM.Descrip
modifier = {
this
.padding(horizontal = TangemTheme.dimens2.x4)
.padding(bottom = TangemTheme.dimens2.x8)
.padding(bottom = 24.dp)
},
otherModifier = {
blockPaddings()