Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-19 18:23:22 +03:00
commit ae15bcfc55
211 changed files with 4715 additions and 3641 deletions

View file

@ -2,9 +2,12 @@ package com.tangem.features.feed.components.market.details.portfolio.impl.model
import com.tangem.common.routing.AppRoute
import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.OfframpAnalyticsEvent
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
@ -12,9 +15,8 @@ import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.offramp.GetOfframpUrlUseCase
import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.features.feed.components.market.details.portfolio.impl.loader.PortfolioData
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.TokenActionsBSContentUM
@ -30,7 +32,9 @@ internal class TokenActionsHandler @AssistedInject constructor(
private val router: Router,
private val clipboardManager: ClipboardManager,
private val uiMessageSender: UiMessageSender,
private val reduxStateHolder: ReduxStateHolder,
private val getOfframpUrlUseCase: GetOfframpUrlUseCase,
private val urlOpener: UrlOpener,
private val analyticsEventHandler: AnalyticsEventHandler,
@Assisted private val currentAppCurrency: Provider<AppCurrency>,
@Assisted private val onHandleQuickAction: (HandledQuickAction) -> Unit,
private val isDemoCardUseCase: IsDemoCardUseCase,
@ -104,12 +108,13 @@ internal class TokenActionsHandler @AssistedInject constructor(
}
private fun onSellClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) {
reduxStateHolder.dispatch(
TradeCryptoAction.Sell(
cryptoCurrencyStatus = cryptoCurrencyData.status,
appCurrencyCode = currentAppCurrency().code,
),
)
getOfframpUrlUseCase(
cryptoCurrencyStatus = cryptoCurrencyData.status,
appCurrencyCode = currentAppCurrency().code,
).onRight { url ->
urlOpener.openUrl(url)
analyticsEventHandler.send(OfframpAnalyticsEvent.ScreenOpened)
}
}
private fun onExchangeClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) {

View file

@ -32,12 +32,10 @@ import com.tangem.features.feed.model.earn.filters.state.EarnFilterNetworkUMConv
import com.tangem.features.feed.model.earn.filters.state.EarnFilterTypeConverter
import com.tangem.features.feed.model.earn.filters.state.EarnFilterTypeUMConverter
import com.tangem.features.feed.model.earn.state.EarnStateController
import com.tangem.features.feed.model.earn.state.transformers.EarnFilterSelectedStateTransformer
import com.tangem.features.feed.model.earn.state.transformers.UpdateBestOpportunitiesStateTransformer
import com.tangem.features.feed.model.earn.state.transformers.UpdateEarnUMInitialStateTransformer
import com.tangem.features.feed.model.earn.state.transformers.UpdateMostlyUsedStateTransformer
import com.tangem.features.feed.model.earn.state.transformers.*
import com.tangem.features.feed.model.earn.statemanager.EarnListBatchFlowManager
import com.tangem.features.feed.model.earn.statemanager.EarnListStateManager
import com.tangem.features.feed.ui.earn.state.EarnBestOpportunitiesUM
import com.tangem.features.feed.ui.earn.state.EarnFilterNetworkUM
import com.tangem.features.feed.ui.earn.state.EarnFilterTypeUM
import com.tangem.features.feed.ui.earn.state.EarnUM
@ -69,8 +67,8 @@ internal class EarnModel @Inject constructor(
private val earnNetworks = MutableStateFlow<EarnNetworks>(Either.Right(emptyList()))
private val earnListConfigProvider = Provider {
createEarnTokensListConfig(
selectedTypeFilter = stateController.value.selectedTypeFilter,
selectedNetworkFilter = stateController.value.selectedNetworkFilter,
selectedTypeFilter = stateController.value.earnFilterUM.selectedTypeFilter,
selectedNetworkFilter = stateController.value.earnFilterUM.selectedNetworkFilter,
earnNetworks = earnNetworks.value,
)
}
@ -104,7 +102,6 @@ internal class EarnModel @Inject constructor(
init {
updateInitialState()
fetchEarnNetworks()
fetchTopEarnTokens()
subscribeOnStoredFilters()
subscribeOnNetworks()
subscribeOnBatchFlow()
@ -117,20 +114,23 @@ internal class EarnModel @Inject constructor(
batchFlowManager.initialLoadingError,
batchFlowManager.paginationStatus,
) { items, error, paginationStatus ->
val hasActiveFilters = state.value.selectedTypeFilter != EarnFilterTypeUM.All ||
state.value.selectedNetworkFilter !is EarnFilterNetworkUM.AllNetworks
error?.let(::handleBestOpportunitiesErrorAnalytics)
val hasActiveFilters = state.value.earnFilterUM.selectedTypeFilter != EarnFilterTypeUM.All ||
state.value.earnFilterUM.selectedNetworkFilter !is EarnFilterNetworkUM.AllNetworks
EarnListStateManager.calculateState(
items = items,
error = error,
paginationStatus = paginationStatus,
hasActiveFilters = hasActiveFilters,
onRetryClick = { batchFlowManager.reload() },
onRetryClick = {
batchFlowManager.reload()
reloadEarnNetworks()
},
onLoadMore = { batchFlowManager.loadMore() },
onClearFiltersClick = ::onClearFiltersClick,
)
}.onEach { bestOpportunitiesState ->
) to error
}.onEach { (bestOpportunitiesState, error) ->
stateController.update(UpdateBestOpportunitiesStateTransformer(bestOpportunitiesState))
error?.let(::handleBestOpportunitiesErrorAnalytics)
}.launchIn(modelScope)
}
@ -156,23 +156,27 @@ internal class EarnModel @Inject constructor(
private fun subscribeOnStoredFilters() {
modelScope.launch(dispatchers.default) {
getEarnFilterUseCase()
.collect { filter ->
val typeFilterUM = EarnFilterTypeConverter().convert(filter.earnFilterType)
val networkFilterUM = EarnFilterNetworkConverter().convert(filter.earnFilterNetwork)
stateController.update(
EarnFilterSelectedStateTransformer(
filterType = typeFilterUM,
filterNetwork = networkFilterUM,
),
)
batchFlowManager.reload()
}
combine(
getEarnFilterUseCase(),
earnNetworks,
) { filter, networks ->
val typeFilterUM = EarnFilterTypeConverter().convert(filter.earnFilterType)
val networkFilterUM = EarnFilterNetworkConverter().convert(filter.earnFilterNetwork)
stateController.update(
EarnFilterSelectedStateTransformer(
filterType = typeFilterUM,
filterNetwork = networkFilterUM,
earnNetworks = networks,
),
)
batchFlowManager.reload()
}.collect()
}
}
private fun fetchTopEarnTokens() {
modelScope.launch(dispatchers.default) {
stateController.update(UpdateMostlyUsedStateLoadingTransformer())
fetchTopEarnTokensUseCase()
}
}
@ -183,13 +187,21 @@ internal class EarnModel @Inject constructor(
}
}
private fun reloadEarnNetworks() {
modelScope.launch(dispatchers.default) {
if (earnNetworks.value.isLeft()) {
fetchEarnNetworks()
}
}
}
/* start of clicks area */
private fun onTypeFilterClick() {
val currentState = state.value
bottomSheetNavigation.activate(
FeedBottomSheetRoute.TypeFilter(
params = EarnTypeFilterComponent.Params(
selectedFilter = EarnFilterTypeUMConverter().convert(currentState.selectedTypeFilter),
selectedFilter = EarnFilterTypeUMConverter().convert(currentState.earnFilterUM.selectedTypeFilter),
onFilterSelected = ::onTypeFilterOptionSelected,
onDismiss = { bottomSheetNavigation.dismiss() },
),
@ -210,7 +222,7 @@ internal class EarnModel @Inject constructor(
}
private fun createNetworkFilters(): List<EarnFilterNetwork> {
val selectedFilter = state.value.selectedNetworkFilter
val selectedFilter = state.value.earnFilterUM.selectedNetworkFilter
return buildList {
add(
EarnFilterNetwork.AllNetworks(
@ -277,11 +289,14 @@ internal class EarnModel @Inject constructor(
modelScope.launch(dispatchers.default) {
setEarnFilterUseCase(
EarnFilter(
earnFilterNetwork = EarnFilterNetworkUMConverter().convert(state.value.selectedNetworkFilter),
earnFilterNetwork = EarnFilterNetworkUMConverter().convert(
value = state.value.earnFilterUM.selectedNetworkFilter,
),
earnFilterType = type,
),
)
bottomSheetNavigation.dismiss()
reloadEarnNetworks()
}
}
@ -290,7 +305,7 @@ internal class EarnModel @Inject constructor(
setEarnFilterUseCase(
EarnFilter(
earnFilterNetwork = filter,
earnFilterType = EarnFilterTypeUMConverter().convert(state.value.selectedTypeFilter),
earnFilterType = EarnFilterTypeUMConverter().convert(state.value.earnFilterUM.selectedTypeFilter),
),
)
}
@ -319,11 +334,13 @@ internal class EarnModel @Inject constructor(
is ApiResponseError.HttpException -> error.code.numericCode to error.message.orEmpty()
else -> null to ""
}
analyticsEventHandler.send(
EarnAnalyticsEvent.BestOpportunitiesLoadError(
code = code,
message = message,
),
)
if (state.value.bestOpportunities !is EarnBestOpportunitiesUM.Error) {
analyticsEventHandler.send(
EarnAnalyticsEvent.BestOpportunitiesLoadError(
code = code,
message = message,
),
)
}
}
}

View file

@ -23,7 +23,9 @@ internal fun createEarnTokensListConfig(
earnNetworks.fold(
ifLeft = { null },
ifRight = { networks ->
networks.filter(EarnNetwork::isAdded).map(EarnNetwork::networkId)
networks.filter(EarnNetwork::isAdded)
.map(EarnNetwork::networkId)
.ifEmpty { listOf(NO_ONE_NETWORK) }
},
)
}
@ -34,4 +36,9 @@ internal fun createEarnTokensListConfig(
networks = networks,
isForEarn = isForEarn,
)
}
}
/**
* This id means that backend has to return empty result
*/
private const val NO_ONE_NETWORK = "-1"

View file

@ -26,8 +26,10 @@ internal class EarnStateController @Inject constructor() {
return EarnUM(
mostlyUsed = EarnListUM.Loading,
bestOpportunities = EarnBestOpportunitiesUM.Loading,
selectedTypeFilter = EarnFilterTypeUM.All,
selectedNetworkFilter = EarnFilterNetworkUM.AllNetworks(isSelected = true),
earnFilterUM = EarnFilterUM(
selectedTypeFilter = EarnFilterTypeUM.All,
selectedNetworkFilter = EarnFilterNetworkUM.AllNetworks(isSelected = true),
),
onBackClick = {},
onNetworkFilterClick = {},
onTypeFilterClick = {},

View file

@ -1,18 +1,24 @@
package com.tangem.features.feed.model.earn.state.transformers
import com.tangem.domain.models.earn.EarnNetworks
import com.tangem.features.feed.ui.earn.state.EarnFilterNetworkUM
import com.tangem.features.feed.ui.earn.state.EarnFilterTypeUM
import com.tangem.features.feed.ui.earn.state.EarnUM
internal class EarnFilterSelectedStateTransformer(
private val earnNetworks: EarnNetworks,
private val filterType: EarnFilterTypeUM,
private val filterNetwork: EarnFilterNetworkUM,
) : EarnUMTransformer {
override fun transform(prevState: EarnUM): EarnUM {
return prevState.copy(
selectedTypeFilter = filterType,
selectedNetworkFilter = filterNetwork,
earnFilterUM = prevState.earnFilterUM.copy(
selectedTypeFilter = filterType,
selectedNetworkFilter = filterNetwork,
isNetworkFilterEnabled = earnNetworks.isRight(),
isTypeFilterEnabled = true,
),
)
}
}

View file

@ -1,13 +0,0 @@
package com.tangem.features.feed.model.earn.state.transformers
import com.tangem.features.feed.ui.earn.state.EarnFilterNetworkUM
import com.tangem.features.feed.ui.earn.state.EarnUM
internal class EarnNetworkFilterSelectedStateTransformer(
private val filter: EarnFilterNetworkUM,
) : EarnUMTransformer {
override fun transform(prevState: EarnUM): EarnUM {
return prevState.copy(selectedNetworkFilter = filter)
}
}

View file

@ -0,0 +1,11 @@
package com.tangem.features.feed.model.earn.state.transformers
import com.tangem.features.feed.ui.earn.state.EarnListUM
import com.tangem.features.feed.ui.earn.state.EarnUM
internal class UpdateMostlyUsedStateLoadingTransformer : EarnUMTransformer {
override fun transform(prevState: EarnUM): EarnUM {
return prevState.copy(mostlyUsed = EarnListUM.Loading)
}
}

View file

@ -91,12 +91,7 @@ internal fun EarnContent(state: EarnUM, modifier: Modifier = Modifier) {
SpacerH(12.dp)
BestOpportunitiesFilters(
state = state.bestOpportunities,
selectedNetworkFilterText = when (state.selectedNetworkFilter) {
is EarnFilterNetworkUM.AllNetworks -> TextReference.Res(R.string.earn_filter_all_networks)
is EarnFilterNetworkUM.MyNetworks -> TextReference.Res(R.string.earn_filter_my_networks)
is EarnFilterNetworkUM.Network -> TextReference.Str(state.selectedNetworkFilter.text)
},
selectedTypeFilterText = state.selectedTypeFilterText,
earnFilterUM = state.earnFilterUM,
onNetworkFilterClick = state.onNetworkFilterClick,
onTypeFilterClick = state.onTypeFilterClick,
)
@ -151,10 +146,12 @@ private fun MostlyUsedContent(state: EarnListUM, onScroll: () -> Unit) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = 16.dp,
vertical = 12.dp,
),
.padding(horizontal = 16.dp, vertical = 12.dp)
.background(
color = TangemTheme.colors.background.action,
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.padding(vertical = 32.dp, horizontal = 12.dp),
contentAlignment = Alignment.Center,
) {
UnableToLoadData(onRetryClick = st.onRetryClicked)
@ -218,17 +215,14 @@ private fun MostlyUsedCard(item: EarnListItemUM, onClick: () -> Unit, modifier:
@Composable
private fun BestOpportunitiesFilters(
state: EarnBestOpportunitiesUM,
selectedNetworkFilterText: TextReference,
selectedTypeFilterText: TextReference,
earnFilterUM: EarnFilterUM,
onNetworkFilterClick: () -> Unit,
onTypeFilterClick: () -> Unit,
) {
when (state) {
is EarnBestOpportunitiesUM.Loading -> FilterButtonsShimmer()
else -> FilterButtons(
selectedNetworkFilterText = selectedNetworkFilterText,
selectedTypeFilterText = selectedTypeFilterText,
isEnabled = state is EarnBestOpportunitiesUM.Content || state is EarnBestOpportunitiesUM.EmptyFiltered,
earnFilterUM = earnFilterUM,
onNetworkFilterClick = onNetworkFilterClick,
onTypeFilterClick = onTypeFilterClick,
)
@ -307,9 +301,7 @@ private fun LazyListScope.bestOpportunitiesItems(state: EarnBestOpportunitiesUM)
@Composable
private fun FilterButtons(
selectedNetworkFilterText: TextReference,
selectedTypeFilterText: TextReference,
isEnabled: Boolean,
earnFilterUM: EarnFilterUM,
onNetworkFilterClick: () -> Unit,
onTypeFilterClick: () -> Unit,
modifier: Modifier = Modifier,
@ -319,10 +311,14 @@ private fun FilterButtons(
) {
SecondarySmallButton(
config = SmallButtonConfig(
text = selectedNetworkFilterText,
text = when (earnFilterUM.selectedNetworkFilter) {
is EarnFilterNetworkUM.AllNetworks -> TextReference.Res(R.string.earn_filter_all_networks)
is EarnFilterNetworkUM.MyNetworks -> TextReference.Res(R.string.earn_filter_my_networks)
is EarnFilterNetworkUM.Network -> TextReference.Str(earnFilterUM.selectedNetworkFilter.text)
},
onClick = onNetworkFilterClick,
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24),
isEnabled = isEnabled,
isEnabled = earnFilterUM.isNetworkFilterEnabled,
),
)
@ -330,10 +326,10 @@ private fun FilterButtons(
SecondarySmallButton(
config = SmallButtonConfig(
text = selectedTypeFilterText,
text = earnFilterUM.selectedTypeFilter.text,
onClick = onTypeFilterClick,
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24),
isEnabled = isEnabled,
isEnabled = earnFilterUM.isTypeFilterEnabled,
),
)
}
@ -496,7 +492,7 @@ private fun EarnContentLoadingPreview() {
) {
EarnContent(
state = previewEarnUM(
mostlyUsed = EarnListUM.Loading,
mostlyUsed = EarnListUM.Error(onRetryClicked = {}),
bestOpportunities = EarnBestOpportunitiesUM.Loading,
),
)
@ -588,8 +584,12 @@ private fun previewEarnUM(
): EarnUM = EarnUM(
mostlyUsed = mostlyUsed,
bestOpportunities = bestOpportunities,
selectedTypeFilter = EarnFilterTypeUM.All,
selectedNetworkFilter = EarnFilterNetworkUM.AllNetworks(isSelected = true),
earnFilterUM = EarnFilterUM(
selectedTypeFilter = EarnFilterTypeUM.All,
selectedNetworkFilter = EarnFilterNetworkUM.AllNetworks(isSelected = true),
isTypeFilterEnabled = true,
isNetworkFilterEnabled = true,
),
onBackClick = {},
onNetworkFilterClick = {},
onTypeFilterClick = {},

View file

@ -0,0 +1,11 @@
package com.tangem.features.feed.ui.earn.state
import androidx.compose.runtime.Immutable
@Immutable
internal data class EarnFilterUM(
val selectedTypeFilter: EarnFilterTypeUM,
val selectedNetworkFilter: EarnFilterNetworkUM,
val isTypeFilterEnabled: Boolean = true,
val isNetworkFilterEnabled: Boolean = true,
)

View file

@ -1,20 +1,14 @@
package com.tangem.features.feed.ui.earn.state
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
@Immutable
internal data class EarnUM(
val mostlyUsed: EarnListUM,
val bestOpportunities: EarnBestOpportunitiesUM,
val selectedTypeFilter: EarnFilterTypeUM,
val selectedNetworkFilter: EarnFilterNetworkUM,
val earnFilterUM: EarnFilterUM,
val onBackClick: () -> Unit,
val onNetworkFilterClick: () -> Unit,
val onTypeFilterClick: () -> Unit,
val onSliderScroll: () -> Unit,
) {
val selectedTypeFilterText: TextReference
get() = selectedTypeFilter.text
}
)

View file

@ -1,6 +1,7 @@
package com.tangem.features.feed.ui.feed.components
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -8,12 +9,18 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.components.buttons.SecondarySmallButton
import com.tangem.core.ui.components.buttons.SmallButtonConfig
import com.tangem.core.ui.extensions.TextReference
@Composable
internal fun Header(onSeeAllClick: () -> Unit, isLoading: Boolean = false, title: @Composable () -> Unit) {
internal fun Header(
onSeeAllClick: () -> Unit,
isLoading: Boolean,
shouldShowSeeAll: Boolean,
title: @Composable () -> Unit,
) {
AnimatedContent(isLoading) { animatedState ->
Row(
modifier = Modifier
@ -25,13 +32,18 @@ internal fun Header(onSeeAllClick: () -> Unit, isLoading: Boolean = false, title
if (animatedState) {
RectangleShimmer(modifier = Modifier.size(width = 104.dp, height = 18.dp))
} else {
title()
SecondarySmallButton(
config = SmallButtonConfig(
text = TextReference.Res(R.string.common_see_all),
onClick = onSeeAllClick,
),
)
Box(modifier = Modifier.weight(1f)) {
title()
}
SpacerW(8.dp)
AnimatedVisibility(shouldShowSeeAll) {
SecondarySmallButton(
config = SmallButtonConfig(
text = TextReference.Res(R.string.common_see_all),
onClick = onSeeAllClick,
),
)
}
}
}
}

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.tangem.core.ui.R
@ -33,10 +34,13 @@ internal fun EarnBlock(onSeeAllClick: () -> Unit, earnListUM: EarnListUM?, modif
text = stringResourceSafe(R.string.markets_earn_common_title),
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
},
onSeeAllClick = onSeeAllClick,
isLoading = earnListUM is EarnListUM.Loading,
shouldShowSeeAll = earnListUM is EarnListUM.Content,
)
SpacerH(12.dp)

View file

@ -4,12 +4,7 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
@ -19,6 +14,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.tangem.common.ui.markets.MarketsListItem
@ -55,9 +51,13 @@ internal fun MarketBlock(marketChart: MarketChartUM?, feedListCallbacks: FeedLis
text = stringResourceSafe(R.string.markets_common_title),
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
},
onSeeAllClick = { feedListCallbacks.onMarketOpenClick(SortByTypeUM.Rating) },
shouldShowSeeAll = currentChart is MarketChartUM.Content,
isLoading = currentChart is MarketChartUM.Loading,
)
SpacerH(12.dp)
@ -88,9 +88,13 @@ internal fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCall
text = stringResourceSafe(R.string.markets_pulse_common_title),
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
},
onSeeAllClick = { onSeeAllClick() },
shouldShowSeeAll = true,
isLoading = marketChartConfig.marketCharts[marketChartConfig.currentSortByType] is MarketChartUM.Loading,
)
LazyRow(

View file

@ -8,8 +8,6 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
@ -19,6 +17,7 @@ import androidx.compose.ui.layout.onFirstVisible
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.news.ArticleCard
@ -65,12 +64,6 @@ internal fun NewsBlock(feedListCallbacks: FeedListCallbacks, news: NewsUM, trend
@Composable
private fun NewsContentBlock(feedListCallbacks: FeedListCallbacks, news: NewsUM, trendingArticle: ArticleConfigUM?) {
val listState = rememberLazyListState()
val articlesReadStatus = remember(news.content) {
news.content.map { it.isViewed }
}
LaunchedEffect(articlesReadStatus) {
listState.requestScrollToItem(0)
}
Column {
Header(
title = {
@ -104,10 +97,14 @@ private fun NewsContentBlock(feedListCallbacks: FeedListCallbacks, news: NewsUM,
}
},
style = TangemTheme.typography.subtitle1,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
},
onSeeAllClick = { feedListCallbacks.onOpenAllNews(false) },
isLoading = news.newsUMState == NewsUMState.LOADING,
shouldShowSeeAll = news.newsUMState == NewsUMState.CONTENT,
)
SpacerH(12.dp)
@ -133,7 +130,7 @@ private fun NewsContentBlock(feedListCallbacks: FeedListCallbacks, news: NewsUM,
) {
itemsIndexed(
items = news.content,
key = { _, article -> article.id },
key = { index, _ -> index },
contentType = { _, _ -> "article" },
) { index, article ->
val articleModifier = if (index == FOURTH_ITEM_INDEX) {
@ -181,10 +178,14 @@ private fun NewsErrorBlock(onRetryClick: () -> Unit) {
text = stringResourceSafe(R.string.common_news),
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
},
onSeeAllClick = {},
shouldShowSeeAll = false,
isLoading = false,
)
SpacerH(12.dp)
BlockCard(