Updated on 2026-08-14
This commit is contained in:
parent
ed89b4bcd0
commit
533c036819
28 changed files with 334 additions and 162 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
)
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue