Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-17 19:20:51 +05:00
parent ed89b4bcd0
commit 533c036819
28 changed files with 334 additions and 162 deletions

View file

@ -38,7 +38,7 @@ internal class MarketsTokenItemConverter(
trendType = value.getTrendType(),
chartData = value.getChartData(),
isUnder100kMarketCap = value.isUnderMarketCapLimit,
stakingRate = value.stakingRate?.format { percent() }?.let {
stakingRate = value.yieldRate?.format { percent() }?.let {
resourceReference(R.string.markets_apy_placeholder, wrappedList(it))
},
updateTimestamp = value.updateTimestamp,

View file

@ -338,6 +338,7 @@ internal class FeedComponentModel @Inject constructor(
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
SortByTypeUM.Staking -> TokenMarketListConfig.Order.Staking
SortByTypeUM.YieldSupply -> TokenMarketListConfig.Order.YieldSupply
}
}

View file

@ -386,6 +386,7 @@ internal class FeedMarketsBatchFlowManager(
TokenMarketListConfig.Order.TopGainers -> SortByTypeUM.TopGainers
TokenMarketListConfig.Order.TopLosers -> SortByTypeUM.TopLosers
TokenMarketListConfig.Order.Staking -> SortByTypeUM.Staking
TokenMarketListConfig.Order.YieldSupply -> SortByTypeUM.YieldSupply
}
}
}

View file

@ -41,6 +41,7 @@ enum class SortByTypeUM(val text: TextReference) {
TopGainers(resourceReference(R.string.markets_sort_by_top_gainers_title)),
TopLosers(resourceReference(R.string.markets_sort_by_top_losers_title)),
Staking(resourceReference(R.string.common_staking)),
YieldSupply(resourceReference(R.string.markets_sort_by_yield_mode_title)),
}
@Immutable

View file

@ -24,6 +24,7 @@ internal sealed class MarketsListAnalyticsEvent(
SortByTypeUM.TopGainers -> "Gainers"
SortByTypeUM.TopLosers -> "Losers"
SortByTypeUM.Staking -> "Staking"
SortByTypeUM.YieldSupply -> "Yield Supply"
},
"Period" to when (interval) {
MarketsListUM.TrendInterval.H24 -> "24h"
@ -32,12 +33,11 @@ internal sealed class MarketsListAnalyticsEvent(
},
),
)
class YieldModePromoShown : MarketsListAnalyticsEvent(event = "Notice - Yield Mode Promo")
class StakingPromoShown : MarketsListAnalyticsEvent(event = "Notice - Staking Promo")
class YieldModePromoClosed : MarketsListAnalyticsEvent(event = "Yield Mode Promo Closed")
class StakingPromoClosed : MarketsListAnalyticsEvent(event = "Staking Promo Closed")
class StakingMoreInfoClicked : MarketsListAnalyticsEvent(event = "Staking More Info")
class YieldModeMoreInfoClicked : MarketsListAnalyticsEvent(event = "Yield Mode More Info")
data class TokenSearched(val tokenFound: Boolean) : MarketsListAnalyticsEvent(
event = "Token Searched",

View file

@ -10,19 +10,20 @@ import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
import com.tangem.domain.markets.GetStakingNotificationMaxApyUseCase
import com.tangem.domain.markets.ShouldShowYieldModeMarketPromoUseCase
import com.tangem.domain.markets.TokenMarket
import com.tangem.domain.markets.TokenMarketListConfig
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.promo.PromoRepository
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.settings.usercountry.models.UserCountryError
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
import com.tangem.features.markets.tokenlist.impl.analytics.MarketsListAnalyticsEvent
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListBatchFlowManager
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListUMStateManager
import com.tangem.features.markets.tokenlist.impl.ui.state.ListUM
import com.tangem.features.markets.tokenlist.impl.ui.state.MarketsListItemUM
import com.tangem.features.markets.tokenlist.impl.ui.state.MarketsListUM.TrendInterval
import com.tangem.features.markets.tokenlist.impl.ui.state.SortByTypeUM
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -31,7 +32,6 @@ import com.tangem.utils.coroutines.saveIn
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.math.BigDecimal
import javax.inject.Inject
private const val UPDATE_QUOTES_TIMER_MILLIS = 60000L
@ -45,7 +45,7 @@ internal class MarketsListModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
getMarketsTokenListFlowUseCase: GetMarketsTokenListFlowUseCase,
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
getStakingNotificationMaxApyUseCase: GetStakingNotificationMaxApyUseCase,
shouldShowYieldModeMarketPromoUseCase: ShouldShowYieldModeMarketPromoUseCase,
private val promoRepository: PromoRepository,
private val getUserCountryUseCase: GetUserCountryUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
@ -69,8 +69,6 @@ internal class MarketsListModel @Inject constructor(
visibleItemsChanged = { visibleItemIds.value = it },
onRetryButtonClicked = { activeListManager.reload() },
onTokenClick = { onTokenUIClicked(it) },
onStakingNotificationClick = { analyticsEventHandler.send(MarketsListAnalyticsEvent.StakingMoreInfoClicked()) },
onStakingNotificationCloseClick = { onStakingNotificationCloseClick() },
onShowTokensUnder100kClicked = { analyticsEventHandler.send(MarketsListAnalyticsEvent.ShowTokens()) },
)
@ -112,53 +110,62 @@ internal class MarketsListModel @Inject constructor(
marketsListUMStateManager.isInSearchStateFlow.flatMapLatest { isInSearchMode ->
if (isInSearchMode) {
combine(
searchMarketsListManager.uiItems,
searchMarketsListManager.isInInitialLoadingErrorState,
searchMarketsListManager.isSearchNotFoundState,
getStakingNotificationMaxApyUseCase(),
getUserCountryUseCase.invoke(),
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState, stakingMaxApy, userCountry ->
flow = searchMarketsListManager.uiItems,
flow2 = searchMarketsListManager.isInInitialLoadingErrorState,
flow3 = searchMarketsListManager.isSearchNotFoundState,
flow4 = shouldShowYieldModeMarketPromoUseCase(
appCurrency = currentAppCurrency.value,
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
),
flow5 = getUserCountryUseCase.invoke(),
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState, isYieldModePromo, userCountry ->
MarketsItemsData(
items = uiItems,
isInErrorState = isInInitialLoadingErrorState,
isSearchNotFound = isSearchNotFoundState,
stakingNotificationMaxApy = stakingMaxApy,
shouldShowYieldModePromo = isYieldModePromo,
userCountry = userCountry,
)
}
} else {
combine(
mainMarketsListManager.uiItems,
mainMarketsListManager.isInInitialLoadingErrorState,
getStakingNotificationMaxApyUseCase(),
getUserCountryUseCase.invoke(),
) { uiItems, isInInitialLoadingErrorState, stakingNotificationMaxApy, userCountry ->
flow = mainMarketsListManager.uiItems,
flow2 = mainMarketsListManager.isInInitialLoadingErrorState,
flow3 = shouldShowYieldModeMarketPromoUseCase(
appCurrency = currentAppCurrency.value,
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
),
flow4 = getUserCountryUseCase.invoke(),
) { uiItems, isInInitialLoadingErrorState, shouldShowYieldModePromo, userCountry ->
MarketsItemsData(
items = uiItems,
isInErrorState = isInInitialLoadingErrorState,
isSearchNotFound = false,
stakingNotificationMaxApy = stakingNotificationMaxApy,
shouldShowYieldModePromo = shouldShowYieldModePromo,
userCountry = userCountry,
)
}
}
}.collect { marketsItemsData ->
val stakingNotificationMaxApy = marketsItemsData.stakingNotificationMaxApy?.takeUnless {
marketsItemsData.userCountry.getOrNull().needApplyFCARestrictions()
}
if (marketsListUMStateManager.state.value.stakingNotificationMaxApy == null &&
stakingNotificationMaxApy != null
) {
analyticsEventHandler.send(MarketsListAnalyticsEvent.StakingPromoShown())
val shouldShowYieldModePromo = marketsItemsData.shouldShowYieldModePromo
if (marketsListUMStateManager.state.value.marketsNotificationUM == null && shouldShowYieldModePromo) {
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModePromoShown())
}
marketsListUMStateManager.onUiItemsChanged(
uiItems = marketsItemsData.items,
isInErrorState = marketsItemsData.isInErrorState,
isSearchNotFound = marketsItemsData.isSearchNotFound,
stakingNotificationMaxApy = marketsItemsData.stakingNotificationMaxApy?.takeUnless {
marketsItemsData.userCountry.getOrNull().needApplyFCARestrictions()
marketsNotificationUM = if (shouldShowYieldModePromo) {
MarketsNotificationUM.YieldSupplyPromo(
onClick = {
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModeMoreInfoClicked())
marketsListUMStateManager.selectedSortByType = SortByTypeUM.YieldSupply
},
onCloseClick = { onYieldModeNotificationCloseClick() },
)
} else {
null
},
)
}
@ -266,6 +273,14 @@ internal class MarketsListModel @Inject constructor(
mainMarketsListManager.reload()
}
fun TrendInterval.toBatchRequestInterval(): TokenMarketListConfig.Interval {
return when (this) {
TrendInterval.H24 -> TokenMarketListConfig.Interval.H24
TrendInterval.D7 -> TokenMarketListConfig.Interval.WEEK
TrendInterval.M1 -> TokenMarketListConfig.Interval.MONTH
}
}
private fun initAnalytics() {
containerBottomSheetState.onEach { bottomSheetState ->
if (bottomSheetState == BottomSheetState.EXPANDED) {
@ -302,10 +317,10 @@ internal class MarketsListModel @Inject constructor(
}.saveIn(updateQuotesJob)
}
private fun onStakingNotificationCloseClick() {
analyticsEventHandler.send(MarketsListAnalyticsEvent.StakingPromoClosed())
private fun onYieldModeNotificationCloseClick() {
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModePromoClosed())
modelScope.launch {
promoRepository.setMarketsStakingNotificationHideClicked()
promoRepository.setMarketsYieldSupplyNotificationHideClicked()
}
}
@ -313,7 +328,7 @@ internal class MarketsListModel @Inject constructor(
val items: ImmutableList<MarketsListItemUM>,
val isInErrorState: Boolean,
val isSearchNotFound: Boolean,
val stakingNotificationMaxApy: BigDecimal?,
val shouldShowYieldModePromo: Boolean,
val userCountry: Either<UserCountryError, UserCountry>,
)
}

View file

@ -0,0 +1,22 @@
package com.tangem.features.markets.tokenlist.impl.model
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.markets.impl.R
internal sealed class MarketsNotificationUM(val config: NotificationConfig) {
data class YieldSupplyPromo(
val onClick: () -> Unit,
val onCloseClick: () -> Unit,
) : MarketsNotificationUM(
config = NotificationConfig(
iconResId = R.drawable.img_yield_supply_in_market_notification,
title = resourceReference(R.string.markets_yield_supply_banner_title),
subtitle = TextReference.EMPTY,
onClick = onClick,
onCloseClick = onCloseClick,
),
)
}

View file

@ -38,7 +38,7 @@ internal class MarketsTokenItemConverter(
trendType = value.getTrendType(),
chartData = value.getChartData(),
isUnder100kMarketCap = value.isUnderMarketCapLimit,
stakingRate = value.stakingRate?.format { percent() }?.let {
stakingRate = value.yieldRate?.format { percent() }?.let {
resourceReference(R.string.markets_apy_placeholder, wrappedList(it))
},
updateTimestamp = value.updateTimestamp,

View file

@ -343,6 +343,7 @@ internal class MarketsListBatchFlowManager(
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
SortByTypeUM.Staking -> TokenMarketListConfig.Order.Staking
SortByTypeUM.YieldSupply -> TokenMarketListConfig.Order.YieldSupply
}
}

View file

@ -8,6 +8,7 @@ import com.tangem.core.ui.event.triggeredEvent
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.markets.impl.R
import com.tangem.features.markets.tokenlist.impl.model.MarketsNotificationUM
import com.tangem.features.markets.tokenlist.impl.ui.state.*
import com.tangem.utils.Provider
import kotlinx.collections.immutable.ImmutableList
@ -16,7 +17,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import java.math.BigDecimal
@Stable
@Suppress("LongParameterList")
@ -26,8 +26,6 @@ internal class MarketsListUMStateManager(
private val visibleItemsChanged: (itemsKeys: List<CryptoCurrency.RawID>) -> Unit,
private val onRetryButtonClicked: () -> Unit,
private val onTokenClick: (MarketsListItemUM) -> Unit,
private val onStakingNotificationClick: () -> Unit,
private val onStakingNotificationCloseClick: () -> Unit,
private val onShowTokensUnder100kClicked: () -> Unit,
) {
@ -92,25 +90,25 @@ internal class MarketsListUMStateManager(
isInErrorState: Boolean,
isSearchNotFound: Boolean,
uiItems: ImmutableList<MarketsListItemUM>,
stakingNotificationMaxApy: BigDecimal?,
marketsNotificationUM: MarketsNotificationUM?,
) {
state.update {
state.update { currentState ->
when {
isInErrorState -> {
it.copy(
currentState.copy(
list = ListUM.LoadingError(onRetryClicked = onRetryButtonClicked),
)
}
isSearchNotFound -> {
it.copy(list = ListUM.SearchNothingFound)
currentState.copy(list = ListUM.SearchNothingFound)
}
uiItems.isEmpty() -> {
it.copy(list = ListUM.Loading)
currentState.copy(list = ListUM.Loading)
}
else -> {
it.updateItems(
currentState.updateItems(
newItems = uiItems,
stakingNotificationMaxApy = stakingNotificationMaxApy,
marketsNotificationUM = marketsNotificationUM,
)
}
}
@ -119,7 +117,7 @@ internal class MarketsListUMStateManager(
private fun MarketsListUM.updateItems(
newItems: ImmutableList<MarketsListItemUM>,
stakingNotificationMaxApy: BigDecimal?,
marketsNotificationUM: MarketsNotificationUM?,
): MarketsListUM {
val currentState = this
@ -131,7 +129,7 @@ internal class MarketsListUMStateManager(
.copy(
showUnder100kTokensNotificationWasHidden = currentState.showUnder100kButtonAlreadyPressed(),
),
stakingNotificationMaxApy = stakingNotificationMaxApy,
marketsNotificationUM = marketsNotificationUM,
)
}
@ -225,12 +223,7 @@ internal class MarketsListUMStateManager(
onOptionClicked = ::onBottomSheetOptionClicked,
),
),
stakingNotificationMaxApy = null,
onStakingNotificationClick = {
onStakingNotificationClick()
selectedSortByType = SortByTypeUM.Staking
},
onStakingNotificationCloseClick = onStakingNotificationCloseClick,
marketsNotificationUM = null,
)
private fun onBottomSheetOptionClicked(sortByTypeUM: SortByTypeUM) {

View file

@ -22,7 +22,6 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@ -38,19 +37,17 @@ import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
import com.tangem.core.ui.components.fields.SearchBar
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.keyboardAsState
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.markets.impl.R
import com.tangem.features.markets.tokenlist.impl.model.MarketsNotificationUM
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListLazyColumn
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListSortByBottomSheet
import com.tangem.features.markets.tokenlist.impl.ui.components.StakingInMarketsPromoNotification
import com.tangem.features.markets.tokenlist.impl.ui.components.YieldSupplyInMarketsPromoNotification
import com.tangem.features.markets.tokenlist.impl.ui.preview.MarketChartListItemPreviewDataProvider
import com.tangem.features.markets.tokenlist.impl.ui.state.ListUM
import com.tangem.features.markets.tokenlist.impl.ui.state.MarketsListUM
@ -58,7 +55,6 @@ import com.tangem.features.markets.tokenlist.impl.ui.state.SortByBottomSheetCont
import com.tangem.features.markets.tokenlist.impl.ui.state.SortByTypeUM
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import java.math.BigDecimal
private const val SHOW_MORE_KEY = "privacyPolicy"
@ -180,38 +176,38 @@ private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modif
)
}
val marketsNotification = state.marketsNotificationUM
AnimatedVisibility(
state.isInSearchMode.not() &&
state.stakingNotificationMaxApy != null &&
state.selectedSortBy != SortByTypeUM.Staking,
state.selectedSortBy != SortByTypeUM.YieldSupply,
) {
val showMore = stringResourceSafe(R.string.common_show_more)
val description = stringResourceSafe(
R.string.markets_staking_banner_description_placeholder,
showMore,
)
val clickableDescription = buildAnnotatedString {
append(description.substringBefore(showMore))
when (marketsNotification) {
is MarketsNotificationUM.YieldSupplyPromo -> {
val description = stringResourceSafe(
R.string.markets_yield_supply_banner_description,
showMore,
)
pushStringAnnotation(SHOW_MORE_KEY, "")
appendColored(showMore, TangemTheme.colors.text.accent)
pop()
val clickableDescription = annotatedReference {
append(description.substringBefore(showMore))
pushStringAnnotation(SHOW_MORE_KEY, "")
appendColored(showMore, TangemTheme.colors.text.accent)
pop()
}
YieldSupplyInMarketsPromoNotification(
config = marketsNotification.config.copy(
subtitle = clickableDescription,
),
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
)
}
else -> { /* no-op */
}
}
StakingInMarketsPromoNotification(
config = NotificationConfig(
iconResId = R.drawable.img_staking_in_market_notification,
title = resourceReference(
R.string.markets_staking_banner_title,
wrappedList(state.stakingNotificationMaxApy.format { percent() }),
),
subtitle = annotatedReference(clickableDescription),
onClick = state.onStakingNotificationClick,
onCloseClick = state.onStakingNotificationCloseClick,
),
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
)
}
}
}
@ -420,9 +416,10 @@ private fun Preview() {
onDismissRequest = {},
content = SortByBottomSheetContentUM(selectedOption = SortByTypeUM.Rating) {},
),
stakingNotificationMaxApy = BigDecimal(0.12345),
onStakingNotificationClick = {},
onStakingNotificationCloseClick = {},
marketsNotificationUM = MarketsNotificationUM.YieldSupplyPromo(
onClick = {},
onCloseClick = {},
),
),
onHeaderSizeChange = {},
bottomSheetState = BottomSheetState.EXPANDED,

View file

@ -0,0 +1,147 @@
package com.tangem.features.markets.tokenlist.impl.ui.components
import android.content.res.Configuration
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.notifications.CloseableIconButton
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveAnnotatedReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
private val bgColor = Color(0x2684B0D7)
private val borderColor = Color(0x2684B0D7)
@Composable
fun YieldSupplyInMarketsPromoNotification(config: NotificationConfig, modifier: Modifier = Modifier) {
var textHeightDp by remember { mutableStateOf(0.dp) }
Box(
modifier = modifier
.fillMaxWidth()
.border(
width = 1.dp,
shape = TangemTheme.shapes.roundedCornersXMedium,
color = borderColor,
)
.clip(shape = TangemTheme.shapes.roundedCornersXMedium)
.background(bgColor)
.clickable { config.onClick?.invoke() },
) {
PromoImage(
iconRes = config.iconResId,
modifier = Modifier.height(textHeightDp),
)
PromoText(
title = config.title,
subtitle = config.subtitle,
onSizeChange = { textHeightDp = it },
)
CloseableIconButton(
onClick = config.onCloseClick,
modifier = Modifier.align(alignment = Alignment.TopEnd),
iconTint = TangemTheme.colors.icon.secondary,
)
}
}
@Composable
private fun PromoImage(@DrawableRes iconRes: Int, modifier: Modifier = Modifier) {
Box(
modifier = modifier.padding(vertical = 8.dp),
contentAlignment = Alignment.Center,
) {
Image(
painter = painterResource(id = iconRes),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier
.requiredWidth(80.dp)
.wrapContentHeight(Alignment.CenterVertically, unbounded = true),
)
}
}
@Composable
private fun PromoText(title: TextReference?, subtitle: TextReference, onSizeChange: (Dp) -> Unit) {
val density = LocalDensity.current
Box(
modifier = Modifier.onSizeChanged {
with(density) { onSizeChange(it.height.toDp()) }
},
) {
TextsBlock(
title = title,
subtitle = subtitle,
modifier = Modifier
.wrapContentHeight()
.align(Alignment.CenterStart)
.padding(start = 80.dp, top = 12.dp, end = 12.dp, bottom = 12.dp),
)
}
}
@Composable
private fun TextsBlock(title: TextReference?, subtitle: TextReference, modifier: Modifier = Modifier) {
Column(modifier = modifier) {
val titleText = title?.resolveReference()
if (titleText != null) {
Text(
text = titleText,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.button,
)
SpacerH(height = TangemTheme.dimens.spacing2)
}
Text(
text = subtitle.resolveAnnotatedReference(),
color = TangemTheme.colors.text.secondary,
style = TangemTheme.typography.caption2,
)
}
}
// region Preview
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun RingPromoNotification_Preview() {
TangemThemePreview {
YieldSupplyInMarketsPromoNotification(
config = NotificationConfig(
title = stringReference("Activate Yield Mode"),
subtitle = stringReference("Power up your assets while supplying them with instant access. Show more"),
iconResId = R.drawable.img_yield_supply_in_market_notification,
onCloseClick = { },
),
)
}
}
// endregion

View file

@ -8,8 +8,8 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.markets.impl.R
import com.tangem.features.markets.tokenlist.impl.model.MarketsNotificationUM
import kotlinx.collections.immutable.ImmutableList
import java.math.BigDecimal
internal data class MarketsListUM(
val list: ListUM,
@ -19,9 +19,7 @@ internal data class MarketsListUM(
val selectedInterval: TrendInterval,
val onIntervalClick: (TrendInterval) -> Unit,
val onSortByButtonClick: () -> Unit,
val stakingNotificationMaxApy: BigDecimal?,
val onStakingNotificationClick: () -> Unit,
val onStakingNotificationCloseClick: () -> Unit,
val marketsNotificationUM: MarketsNotificationUM?,
) {
val isInSearchMode
get() = searchBar.isActive
@ -40,6 +38,7 @@ enum class SortByTypeUM(val text: TextReference) {
TopGainers(resourceReference(R.string.markets_sort_by_top_gainers_title)),
TopLosers(resourceReference(R.string.markets_sort_by_top_losers_title)),
Staking(resourceReference(R.string.common_staking)),
YieldSupply(resourceReference(R.string.yield_module_earn_sheet_title)),
}
@Immutable