Updated on 2026-08-14
This commit is contained in:
parent
aba2f43c34
commit
de3e88227a
10 changed files with 91 additions and 4 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 -> {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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>()
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ dependencies {
|
|||
|
||||
/** Feature */
|
||||
implementation(projects.features.yieldSupply.api)
|
||||
implementation(projects.features.marketing.api)
|
||||
|
||||
/** Core */
|
||||
implementation(projects.core.configToggles)
|
||||
|
|
@ -59,6 +60,7 @@ dependencies {
|
|||
implementation(projects.domain.feedback)
|
||||
implementation(projects.domain.balanceHiding.models)
|
||||
implementation(projects.domain.balanceHiding)
|
||||
implementation(projects.domain.marketing.models)
|
||||
implementation(projects.libs.crypto)
|
||||
|
||||
/** Compose */
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.tangem.core.ui.extensions.stringResourceSafe
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.YieldSupplyTestTags
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.marketing.api.MarketingBannerComponent
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
import com.tangem.features.yield.supply.impl.active.model.YieldSupplyActiveModel
|
||||
|
|
@ -40,6 +41,7 @@ internal class DefaultYieldSupplyActiveComponent @AssistedInject constructor(
|
|||
@Assisted private val appComponentContext: AppComponentContext,
|
||||
@Assisted private val params: YieldSupplyActiveComponent.Params,
|
||||
private val appRouter: AppRouter,
|
||||
private val marketingBannerComponentFactory: MarketingBannerComponent.Factory,
|
||||
) : YieldSupplyActiveComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: YieldSupplyActiveModel = getOrCreateModel(params = params)
|
||||
|
|
@ -51,6 +53,14 @@ internal class DefaultYieldSupplyActiveComponent @AssistedInject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
private val marketingBannerComponent = marketingBannerComponentFactory.create(
|
||||
context = child("marketingBanner"),
|
||||
params = MarketingBannerComponent.Params.Standalone(
|
||||
requestFlow = model.marketingRequest,
|
||||
onDeeplinkClick = model::onMarketingBannerDeeplink,
|
||||
),
|
||||
)
|
||||
|
||||
private val bottomSheetSlot = childSlot(
|
||||
key = "yieldSupplyActiveStack",
|
||||
source = model.slotNavigation,
|
||||
|
|
@ -85,6 +95,7 @@ internal class DefaultYieldSupplyActiveComponent @AssistedInject constructor(
|
|||
state = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
chartComponent = chartComponent,
|
||||
marketingBannerComponent = marketingBannerComponent,
|
||||
onReadMoreClick = model::onReadMoreClick,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,15 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.deeplink.resolveMarketingDeeplink
|
||||
import com.tangem.common.routing.deeplink.toContextualRoute
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.domain.marketing.models.MarketingScreen
|
||||
import com.tangem.domain.stories.models.StoryContentIds
|
||||
import com.tangem.domain.yield.supply.models.YieldBoostStatus
|
||||
import com.tangem.domain.yield.supply.promo.usecase.GetYieldBoostStatusUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.*
|
||||
import com.tangem.features.marketing.api.MarketingBannerRequest
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent
|
||||
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
|
|
@ -96,6 +101,15 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
|||
private val cryptoCurrency = cryptoCurrencyStatusFlow.value.currency
|
||||
private var appCurrency = AppCurrency.Default
|
||||
|
||||
val marketingRequest: Flow<MarketingBannerRequest?> = flowOf(
|
||||
MarketingBannerRequest(
|
||||
screen = MarketingScreen.Yield(
|
||||
networkId = params.cryptoCurrency.network.rawId,
|
||||
contractAddress = (params.cryptoCurrency as? CryptoCurrency.Token)?.contractAddress.orEmpty(),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val uiState: StateFlow<YieldSupplyActiveContentUM>
|
||||
field = MutableStateFlow(
|
||||
YieldSupplyActiveContentUM(
|
||||
|
|
@ -145,6 +159,16 @@ internal class YieldSupplyActiveModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onMarketingBannerDeeplink(deeplink: String): Boolean {
|
||||
val route = resolveMarketingDeeplink(deeplink).toContextualRoute(
|
||||
userWalletId = userWalletId,
|
||||
currency = cryptoCurrency,
|
||||
screenSource = AnalyticsParam.ScreensSources.Token,
|
||||
) ?: return false
|
||||
appRouter.push(route)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDismissClick() {
|
||||
if (!transactionInProgressFlow.value) {
|
||||
slotNavigation.dismiss()
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
|
|||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemeRedesign
|
||||
import com.tangem.features.yield.supply.impl.R
|
||||
import com.tangem.features.yield.supply.impl.active.entity.YieldSupplyActiveContentUM
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -51,6 +52,7 @@ internal fun YieldSupplyActiveContent(
|
|||
isBalanceHidden: Boolean,
|
||||
onReadMoreClick: () -> Unit,
|
||||
chartComponent: ComposableContentComponent,
|
||||
marketingBannerComponent: ComposableContentComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -88,6 +90,10 @@ internal fun YieldSupplyActiveContent(
|
|||
}
|
||||
}
|
||||
|
||||
TangemThemeRedesign {
|
||||
marketingBannerComponent.Content(Modifier.fillMaxWidth())
|
||||
}
|
||||
|
||||
AnimatedVisibility(state.notifications.isNotEmpty()) {
|
||||
val notifications = remember(state.notifications) { state.notifications }
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
|
|
@ -418,6 +424,7 @@ private fun YieldSupplyActiveBottomSheet_Preview(
|
|||
state = params,
|
||||
isBalanceHidden = true,
|
||||
chartComponent = ComposableContentComponent.EMPTY,
|
||||
marketingBannerComponent = ComposableContentComponent.EMPTY,
|
||||
onReadMoreClick = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue