Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-12 10:01:22 +01:00
parent 9b134d7058
commit bc76eaa64a
46 changed files with 764 additions and 154 deletions

View file

@ -1,6 +0,0 @@
package com.tangem.features.feed.entry
enum class BottomSheetState {
EXPANDED,
COLLAPSED,
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.features.feed.entry.BottomSheetState
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
@Stable
interface FeedEntryComponent {

View file

@ -51,6 +51,7 @@ dependencies {
implementation(projects.domain.settings)
implementation(projects.domain.notifications.models)
implementation(projects.domain.transaction)
implementation(projects.domain.news)
// FIXME [REDACTED_TASK_KEY]
// Remove the "Buy" and "Sell" actions from the redux middleware.
@ -86,6 +87,7 @@ dependencies {
implementation(projects.core.analytics)
implementation(projects.core.analytics.models)
implementation(projects.core.navigation)
implementation(projects.core.utils)
/* Common */
implementation(projects.common.ui)

View file

@ -1,10 +1,13 @@
package com.tangem.features.feed.components
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack
@ -13,11 +16,13 @@ import com.arkivanov.decompose.value.Value
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.navigation.inner.InnerRouter
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.decompose.ComposableModularContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.features.feed.components.market.details.DefaultMarketsTokenDetailsComponent
import com.tangem.features.feed.entry.BottomSheetState
import com.tangem.features.feed.entry.components.FeedEntryComponent
import com.tangem.features.feed.ui.EntryBottomSheetContent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -35,7 +40,7 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor(
popCallback = { onChildBack() },
)
private val stack: Value<ChildStack<FeedEntryChildFactory.Child, Any>> = childStack(
private val stack: Value<ChildStack<FeedEntryChildFactory.Child, ComposableModularContentComponent>> = childStack(
key = "main",
source = stackNavigation,
serializer = FeedEntryChildFactory.Child.serializer(),
@ -59,7 +64,16 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor(
onHeaderSizeChange: (Dp) -> Unit,
modifier: Modifier,
) {
bottomSheetState // TODO will be continued in next tasks.
val stackState by stack.subscribeAsState()
BackHandler(enabled = bottomSheetState.value == BottomSheetState.EXPANDED) {
onChildBack()
}
EntryBottomSheetContent(
stackState = stackState,
onHeaderSizeChange = onHeaderSizeChange,
)
}
private fun marketsListTokenSelected(token: TokenMarketParams, appCurrency: AppCurrency) {

View file

@ -3,6 +3,7 @@ package com.tangem.features.feed.components
import androidx.compose.runtime.Immutable
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.navigation.Route
import com.tangem.core.ui.decompose.ComposableModularContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.features.feed.components.feed.DefaultFeedComponent
@ -11,8 +12,9 @@ import com.tangem.features.feed.components.market.list.DefaultMarketsTokenListCo
import com.tangem.features.feed.components.news.details.DefaultNewsDetailsComponent
import com.tangem.features.feed.components.news.list.DefaultNewsListComponent
import kotlinx.serialization.Serializable
import javax.inject.Inject
internal class FeedEntryChildFactory {
internal class FeedEntryChildFactory @Inject constructor() {
@Serializable
@Immutable
@ -43,7 +45,7 @@ internal class FeedEntryChildFactory {
child: Child,
appComponentContext: AppComponentContext,
onTokenClick: (TokenMarketParams, AppCurrency) -> Unit,
): Any {
): ComposableModularContentComponent {
return when (child) {
is Child.TokenDetails -> {
DefaultMarketsTokenDetailsComponent(

View file

@ -1,23 +1,37 @@
package com.tangem.features.feed.components.feed
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableModularContentComponent
import com.tangem.features.feed.model.feed.FeedComponentModel
import com.tangem.features.feed.ui.feed.FeedListContent
import com.tangem.features.feed.ui.feed.FeedListHeader
internal class DefaultFeedComponent(
appComponentContext: AppComponentContext,
) : ComposableModularContentComponent, AppComponentContext by appComponentContext {
private val feedComponentModel = getOrCreateModel<FeedComponentModel>()
@Composable
override fun Title() {
val state by feedComponentModel.state.collectAsStateWithLifecycle()
FeedListHeader(state.searchBar)
}
@Composable
override fun Content(modifier: Modifier) {
val state by feedComponentModel.state.collectAsStateWithLifecycle()
FeedListContent(
modifier = modifier,
state = state,
)
}
@Composable
override fun Footer() {
}
override fun Footer() = Unit
}

View file

@ -0,0 +1,18 @@
package com.tangem.features.feed.di
import com.tangem.features.feed.components.DefaultFeedEntryComponent
import com.tangem.features.feed.entry.components.FeedEntryComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface ComponentModule {
@Binds
@Singleton
fun bindFeedEntryComponent(factory: DefaultFeedEntryComponent.Factory): FeedEntryComponent.Factory
}

View file

@ -0,0 +1,20 @@
package com.tangem.features.feed.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.features.feed.model.feed.FeedComponentModel
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@Module
@InstallIn(ModelComponent::class)
internal interface ModelModule {
@Binds
@IntoMap
@ClassKey(FeedComponentModel::class)
fun bindsFeedComponentModel(model: FeedComponentModel): Model
}

View file

@ -0,0 +1,103 @@
package com.tangem.features.feed.model.feed
import androidx.compose.runtime.Stable
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.domain.news.usecase.FetchTrendingNewsUseCase
import com.tangem.domain.news.usecase.ManageTrendingNewsUseCase
import com.tangem.features.feed.impl.R
import com.tangem.features.feed.ui.feed.state.*
import com.tangem.features.feed.ui.market.state.SortByTypeUM
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.toPersistentHashMap
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import javax.inject.Inject
@Stable
@ModelScoped
internal class FeedComponentModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val fetchTrendingNewsUseCase: FetchTrendingNewsUseCase,
private val manageTrendingNewsUseCase: ManageTrendingNewsUseCase,
) : Model() {
private val _state = MutableStateFlow(initialState())
val state = _state.asStateFlow()
private val searchBarStateFactory by lazy(LazyThreadSafetyMode.NONE) {
SearchBarStateFactory(
currentStateProvider = Provider { _state.value },
onStateUpdate = { newState -> _state.update { newState } },
)
}
private val trendingNewsStateFactory by lazy(LazyThreadSafetyMode.NONE) {
TrendingNewsStateFactory(
currentStateProvider = Provider { _state.value },
onStateUpdate = { newState -> _state.update { newState } },
)
}
init {
modelScope.launch(dispatchers.default) {
fetchTrendingNewsUseCase()
subscribeOnTrendingNews()
}
_state.update { feedListUM ->
feedListUM.copy(
searchBar = _state.value.searchBar.copy(onQueryChange = searchBarStateFactory::onSearchQueryChange),
)
}
}
private suspend fun subscribeOnTrendingNews() {
manageTrendingNewsUseCase().collect { articles ->
trendingNewsStateFactory.updateTrendingNewsState(articles)
}
}
private fun initialState(): FeedListUM {
return FeedListUM(
currentDate = getCurrentDate(),
searchBar = SearchBarUM(
placeholderText = resourceReference(R.string.markets_search_header_title),
query = "",
onQueryChange = {},
isActive = false,
onActiveChange = { },
),
feedListCallbacks = FeedListCallbacks(
onSearchClick = {},
onMarketOpenClick = {},
onArticleClick = {},
onOpenAllNews = {},
onMarketItemClick = {},
onSortTypeClick = {},
),
news = NewsUM.Loading,
trendingArticle = null,
marketChartConfig = MarketChartConfig(
marketCharts = buildMap {
SortByTypeUM.entries.forEach {
put(it, MarketChartUM.Loading)
} // TODO in [REDACTED_TASK_KEY] add correct sorting
}.toPersistentHashMap(),
currentSortByType = SortByTypeUM.TopGainers,
),
)
}
private fun getCurrentDate(): String {
val localDate = DateTime(DateTime.now(), DateTimeZone.getDefault())
return DateTimeFormatters.formatDate(formatter = DateTimeFormatters.dateDMMM, date = localDate)
}
}

View file

@ -0,0 +1,48 @@
package com.tangem.features.feed.ui
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.arkivanov.decompose.router.stack.ChildStack
import com.tangem.core.ui.decompose.ComposableModularContentComponent
import com.tangem.features.feed.components.FeedEntryChildFactory
@Composable
internal fun EntryBottomSheetContent(
stackState: ChildStack<FeedEntryChildFactory.Child, ComposableModularContentComponent>,
onHeaderSizeChange: (Dp) -> Unit,
) {
val density = LocalDensity.current
Scaffold(
contentWindowInsets = WindowInsets(0.dp),
topBar = {
AnimatedContent(
targetState = stackState.active.instance,
modifier = Modifier.onGloballyPositioned { coordinates ->
if (coordinates.size.height > 0) {
with(density) {
onHeaderSizeChange(coordinates.size.height.toDp())
}
}
},
) { currentState ->
currentState.Title()
}
},
content = { contentPadding ->
AnimatedContent(
stackState.active.instance,
) { currentState ->
currentState.Content(modifier = Modifier.padding(contentPadding))
}
},
)
}

View file

@ -22,25 +22,25 @@ import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.tangem.common.ui.news.ArticleCard
import com.tangem.common.ui.news.ArticleConfigUM
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.components.block.TangemBlockCardColors
import com.tangem.core.ui.components.buttons.SecondarySmallButton
import com.tangem.core.ui.components.buttons.SmallButtonConfig
import com.tangem.core.ui.components.fields.SearchBar
import com.tangem.common.ui.news.ArticleCard
import com.tangem.common.ui.news.ArticleConfigUM
import com.tangem.core.ui.components.fields.TangemSearchBarDefaults
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
@ -48,51 +48,44 @@ import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.feed.ui.feed.preview.FeedListPreviewDataProvider.createFeedPreviewState
import com.tangem.features.feed.ui.feed.state.FeedListCallbacks
import com.tangem.features.feed.ui.feed.state.FeedListUM
import com.tangem.features.feed.ui.feed.state.MarketChartConfig
import com.tangem.features.feed.ui.feed.state.MarketChartUM
import com.tangem.features.feed.ui.feed.state.*
import com.tangem.features.feed.ui.market.components.MarketsListItem
import com.tangem.features.feed.ui.market.components.MarketsListItemPlaceholder
import com.tangem.features.feed.ui.market.state.MarketsListItemUM
import com.tangem.features.feed.ui.market.state.SortByTypeUM
import kotlinx.collections.immutable.ImmutableList
@Composable
internal fun FeedList(state: FeedListUM, onHeaderSizeChange: (Dp) -> Unit, modifier: Modifier = Modifier) {
val density = LocalDensity.current
internal fun FeedListHeader(searchBarUM: SearchBarUM, modifier: Modifier = Modifier) {
val background = LocalMainBottomSheetColor.current.value
SearchBar(
modifier = modifier
.drawBehind { drawRect(background) }
.padding(horizontal = 16.dp)
.padding(bottom = 12.dp),
state = searchBarUM,
colors = TangemSearchBarDefaults.defaultTextFieldColors.copy(
focusedContainerColor = TangemTheme.colors.field.focused,
unfocusedContainerColor = TangemTheme.colors.field.focused,
),
)
}
@Composable
internal fun FeedListContent(state: FeedListUM, modifier: Modifier = Modifier) {
val background = LocalMainBottomSheetColor.current.value
Column(
modifier = modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.drawBehind { drawRect(background) },
) {
SearchBar(
modifier = Modifier
.drawBehind { drawRect(background) }
.padding(
start = 16.dp,
end = 16.dp,
bottom = 8.dp,
)
.onGloballyPositioned { coordinates ->
if (coordinates.size.height > 0) {
with(density) {
onHeaderSizeChange(coordinates.size.height.toDp())
}
}
}
.padding(bottom = 4.dp),
state = state.searchBar,
)
SpacerH(20.dp)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
.padding(horizontal = 20.dp),
text = stringResourceSafe(R.string.feed_market_and_news),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
@ -100,7 +93,7 @@ internal fun FeedList(state: FeedListUM, onHeaderSizeChange: (Dp) -> Unit, modif
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
.padding(horizontal = 20.dp),
text = state.currentDate,
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.tertiary,
@ -156,6 +149,17 @@ private fun MarketBlock(marketChartConfig: MarketChartConfig, feedListCallbacks:
@Composable
private fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCallbacks: FeedListCallbacks) {
if (marketChartConfig.marketCharts.isNotEmpty()) {
Header(
title = {
Text(
text = stringResourceSafe(R.string.markets_common_title),
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
)
},
onSeeAllClick = { feedListCallbacks.onMarketOpenClick(marketChartConfig.currentSortByType) },
)
LazyRow(
modifier = Modifier.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
@ -175,17 +179,6 @@ private fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCallb
}
}
Header(
title = {
Text(
text = stringResourceSafe(R.string.markets_common_title),
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
)
},
onSeeAllClick = { feedListCallbacks.onMarketOpenClick(marketChartConfig.currentSortByType) },
)
SpacerH(12.dp)
AnimatedContent(
@ -207,18 +200,38 @@ private fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCallb
@Suppress("CanBeNonNullable")
@Composable
private fun NewsBlock(
private fun NewsBlock(feedListCallbacks: FeedListCallbacks, news: NewsUM, trendingArticle: ArticleConfigUM?) {
AnimatedContent(news) { newsUM ->
when (newsUM) {
is NewsUM.Content -> {
if (newsUM.content.isNotEmpty()) {
NewsContentBlock(
feedListCallbacks = feedListCallbacks,
news = newsUM,
trendingArticle = trendingArticle,
)
}
}
NewsUM.Loading -> {
NewsLoadingBlock()
}
}
}
}
@Composable
private fun NewsContentBlock(
feedListCallbacks: FeedListCallbacks,
news: ImmutableList<ArticleConfigUM>,
news: NewsUM.Content,
trendingArticle: ArticleConfigUM?,
) {
if (news.isNotEmpty()) {
Column {
Header(
title = {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = stringResourceSafe(R.string.common_news),
style = TangemTheme.typography.subtitle1,
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
)
@ -244,13 +257,12 @@ private fun NewsBlock(
append(stringResourceSafe(R.string.feed_tangem_ai))
}
},
style = TangemTheme.typography.subtitle1,
style = TangemTheme.typography.h3,
)
}
},
onSeeAllClick = feedListCallbacks.onOpenAllNews,
)
SpacerH(12.dp)
trendingArticle?.let { article ->
@ -260,8 +272,8 @@ private fun NewsBlock(
.padding(horizontal = 16.dp),
articleConfigUM = article,
onArticleClick = { feedListCallbacks.onArticleClick(article.id) },
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
)
SpacerH(12.dp)
}
@ -272,13 +284,16 @@ private fun NewsBlock(
state = rememberLazyListState(),
) {
items(
items = news,
items = news.content,
key = ArticleConfigUM::id,
) { article ->
ArticleCard(
articleConfigUM = article,
onArticleClick = { feedListCallbacks.onArticleClick(article.id) },
modifier = Modifier.size(164.dp),
modifier = Modifier
.height(164.dp)
.widthIn(max = 216.dp),
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
)
}
}
@ -292,6 +307,7 @@ private fun Header(title: @Composable () -> Unit, onSeeAllClick: () -> Unit) {
.fillMaxWidth()
.padding(horizontal = 20.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
title()
@ -310,7 +326,10 @@ private fun Charts(
onItemClick: (MarketsListItemUM) -> Unit,
modifier: Modifier = Modifier,
) {
BlockCard(modifier) {
BlockCard(
modifier = modifier,
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
) {
Column(
modifier = Modifier
.fillMaxWidth()
@ -380,9 +399,6 @@ private val LinearGradientSecondPart = Color(0xFFE05AED)
@Composable
private fun FeedListPreview() {
TangemThemePreview {
FeedList(
state = createFeedPreviewState(),
onHeaderSizeChange = {},
)
FeedListContent(state = createFeedPreviewState())
}
}

View file

@ -0,0 +1,100 @@
package com.tangem.features.feed.ui.feed
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.news.DefaultLoadingArticle
import com.tangem.common.ui.news.TrendingLoadingArticle
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.feed.ui.market.components.MarketsListItemPlaceholder
@Composable
internal fun MarketLoadingBlock() {
RectangleShimmer(
modifier = Modifier
.padding(start = 16.dp)
.size(width = 104.dp, height = 18.dp),
)
SpacerH(12.dp)
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
SpacerH(32.dp)
}
@Composable
internal fun MarketPulseLoadingBlock() {
RectangleShimmer()
SpacerH(8.dp)
LazyRow(
modifier = Modifier.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
state = rememberLazyListState(),
) {
items(DEFAULT_CHART_SIZE_IN_MARKET) {
RectangleShimmer(modifier = Modifier.size(width = 124.dp, height = 36.dp))
}
}
SpacerH(12.dp)
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
SpacerH(32.dp)
}
@Composable
internal fun NewsLoadingBlock() {
Column {
RectangleShimmer()
SpacerH(12.dp)
TrendingLoadingArticle(modifier = Modifier.padding(horizontal = 16.dp))
SpacerH(12.dp)
LazyRow(
verticalAlignment = Alignment.CenterVertically,
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
state = rememberLazyListState(),
) {
items(DEFAULT_CHART_SIZE_IN_MARKET) {
DefaultLoadingArticle()
}
}
}
}
@Composable
private fun ChartsLoading(modifier: Modifier = Modifier) {
BlockCard(modifier) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
) {
repeat(DEFAULT_CHART_SIZE_IN_MARKET) {
MarketsListItemPlaceholder()
}
}
}
}
private const val DEFAULT_CHART_SIZE_IN_MARKET = 5
@Preview(showBackground = true)
@Composable
private fun FeedListLoadingPreview() {
TangemThemePreview {
Column {
NewsLoadingBlock()
SpacerH(10.dp)
MarketLoadingBlock()
SpacerH(10.dp)
MarketPulseLoadingBlock()
}
}
}

View file

@ -1,16 +1,15 @@
package com.tangem.features.feed.ui.feed.preview
import com.tangem.common.ui.charts.state.MarketChartRawData
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.common.ui.news.ArticleConfigUM
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.feed.impl.R
import com.tangem.features.feed.ui.feed.state.*
import com.tangem.features.feed.ui.market.state.MarketsListItemUM
import com.tangem.features.feed.ui.market.state.SortByTypeUM
@ -39,7 +38,7 @@ internal object FeedListPreviewDataProvider {
onMarketItemClick = {},
onSortTypeClick = {},
),
news = articles.filter { it.isTrending.not() }.toImmutableList(),
news = NewsUM.Content(articles.filter { it.isTrending.not() }.toImmutableList()),
trendingArticle = articles.first { it.isTrending },
marketChartConfig = MarketChartConfig(
marketCharts = createMarketCharts(marketItems, includeErrorState = false),
@ -165,7 +164,7 @@ internal object FeedListPreviewDataProvider {
LabelUM(
text = TextReference.Str("BTC"),
leadingContent = LabelLeadingContentUM.Token(
icon = R.drawable.img_btc_22,
iconUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/euro-coin.png",
),
),
LabelUM(TextReference.Str("Regulation")),

View file

@ -14,7 +14,7 @@ internal data class FeedListUM(
val currentDate: String,
val searchBar: SearchBarUM,
val feedListCallbacks: FeedListCallbacks,
val news: ImmutableList<ArticleConfigUM>,
val news: NewsUM,
val trendingArticle: ArticleConfigUM?,
val marketChartConfig: MarketChartConfig,
)
@ -28,6 +28,12 @@ internal data class FeedListCallbacks(
val onSortTypeClick: (SortByTypeUM) -> Unit,
)
@Immutable
internal sealed interface NewsUM {
data object Loading : NewsUM
data class Content(val content: ImmutableList<ArticleConfigUM>) : NewsUM
}
internal data class MarketChartConfig(
val marketCharts: ImmutableMap<SortByTypeUM, MarketChartUM>,
val currentSortByType: SortByTypeUM = SortByTypeUM.TopGainers,
@ -49,7 +55,7 @@ internal sealed interface MarketChartUM {
data class LoadingError(val onRetryClicked: () -> Unit) : MarketChartUM
}
data class SortChartConfigUM(
internal data class SortChartConfigUM(
val sortByType: SortByTypeUM,
val isSelected: Boolean,
)

View file

@ -0,0 +1,24 @@
package com.tangem.features.feed.ui.feed.state
import com.tangem.utils.Provider
internal class SearchBarStateFactory(
private val currentStateProvider: Provider<FeedListUM>,
private val onStateUpdate: (FeedListUM) -> Unit,
) {
val searchQuery: String
get() = currentStateProvider().searchBar.query
fun onSearchQueryChange(query: String) {
val currentState = currentStateProvider()
onStateUpdate(
currentState.copy(
searchBar = currentState.searchBar.copy(
query = query,
isActive = query.isNotEmpty(),
),
),
)
}
}

View file

@ -0,0 +1,83 @@
package com.tangem.features.feed.ui.feed.state
import com.tangem.common.ui.news.ArticleConfigUM
import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.data.common.currency.getTokenIconUrlFromDefaultHost
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.news.ShortArticle
import com.tangem.utils.Provider
import kotlinx.collections.immutable.toPersistentList
import kotlinx.collections.immutable.toPersistentSet
internal class TrendingNewsStateFactory(
private val currentStateProvider: Provider<FeedListUM>,
private val onStateUpdate: (FeedListUM) -> Unit,
) {
fun updateTrendingNewsState(news: List<ShortArticle>) {
val trendingArticleIndex = news.indexOfFirst { it.isTrending }
val trendingArticle = if (trendingArticleIndex != -1) news[trendingArticleIndex] else null
val commonArticles = if (trendingArticleIndex != -1) {
news.toMutableList().apply { removeAt(trendingArticleIndex) }
} else {
news
}
val currentState = currentStateProvider()
onStateUpdate(
currentState.copy(
trendingArticle = trendingArticle?.let { article ->
ArticleConfigUM(
id = article.id,
title = article.title,
score = article.score,
isTrending = true,
tags = article.categories.map { category ->
LabelUM(text = TextReference.Str(category.name))
}.plus(
article.relatedTokens.map { token ->
LabelUM(
text = TextReference.Str(token.symbol),
leadingContent = LabelLeadingContentUM.Token(
iconUrl = getTokenIconUrlFromDefaultHost(
tokenId = CryptoCurrency.RawID(token.id),
),
),
)
},
).toPersistentSet(),
createdAt = "1 min ago", // TODO in [REDACTED_TASK_KEY]
isViewed = article.viewed,
)
},
news = NewsUM.Content(
commonArticles.map { article ->
ArticleConfigUM(
id = article.id,
title = article.title,
score = article.score,
isTrending = false,
tags = article.categories.map { category ->
LabelUM(text = TextReference.Str(category.name))
}.plus(
article.relatedTokens.map { token ->
LabelUM(
text = TextReference.Str(token.symbol),
leadingContent = LabelLeadingContentUM.Token(
iconUrl = getTokenIconUrlFromDefaultHost(
tokenId = CryptoCurrency.RawID(token.id),
),
),
)
},
).toPersistentSet(),
createdAt = "1 min ago", // TODO in [REDACTED_TASK_KEY]
isViewed = article.viewed,
)
}.toPersistentList(),
),
),
)
}
}

View file

@ -6,10 +6,10 @@ import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.features.markets.entry.BottomSheetState
import kotlinx.serialization.Serializable
@Stable

View file

@ -1,6 +0,0 @@
package com.tangem.features.markets.entry
enum class BottomSheetState {
EXPANDED,
COLLAPSED,
}

View file

@ -6,6 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
@Stable
interface MarketsEntryComponent {

View file

@ -7,10 +7,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.features.markets.entry.BottomSheetState
@Stable
interface MarketsTokenListComponent : ComposableContentComponent {

View file

@ -11,6 +11,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
@ -21,7 +22,6 @@ import com.tangem.features.markets.details.impl.analytics.MarketDetailsAnalytics
import com.tangem.features.markets.details.impl.model.MarketsTokenDetailsModel
import com.tangem.features.markets.details.impl.model.state.TokenNetworksState
import com.tangem.features.markets.details.impl.ui.MarketsTokenDetailsContent
import com.tangem.features.markets.entry.BottomSheetState
import com.tangem.features.markets.portfolio.api.MarketsPortfolioComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory

View file

@ -15,10 +15,10 @@ import com.arkivanov.decompose.value.Value
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.navigation.inner.InnerRouter
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.features.markets.details.MarketsTokenDetailsComponent
import com.tangem.features.markets.entry.BottomSheetState
import com.tangem.features.markets.entry.MarketsEntryComponent
import com.tangem.features.markets.entry.impl.MarketsEntryChildFactory.Child
import com.tangem.features.markets.entry.impl.ui.EntryBottomSheetContent

View file

@ -15,10 +15,10 @@ import com.arkivanov.decompose.extensions.compose.stack.Children
import com.arkivanov.decompose.extensions.compose.stack.animation.slide
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
import com.arkivanov.decompose.router.stack.ChildStack
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.markets.details.MarketsTokenDetailsComponent
import com.tangem.features.markets.entry.BottomSheetState
import com.tangem.features.markets.entry.impl.MarketsEntryChildFactory
import com.tangem.features.markets.tokenlist.MarketsTokenListComponent

View file

@ -16,12 +16,12 @@ import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRoute.MarketsTokenDetails.AnalyticsParams
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.WindowInsetsZero
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.markets.toSerializableParam
import com.tangem.features.markets.entry.BottomSheetState
import com.tangem.features.markets.tokenlist.MarketsTokenListComponent
import com.tangem.features.markets.tokenlist.impl.model.MarketsListModel
import com.tangem.features.markets.tokenlist.impl.ui.MarketsList

View file

@ -6,6 +6,7 @@ import arrow.core.getOrElse
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
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
@ -17,7 +18,6 @@ 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.entry.BottomSheetState
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

View file

@ -30,6 +30,7 @@ import com.tangem.core.ui.components.Keyboard
import com.tangem.core.ui.components.SpacerH12
import com.tangem.core.ui.components.SpacerH8
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.components.buttons.SecondarySmallButton
import com.tangem.core.ui.components.buttons.SmallButtonConfig
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
@ -46,7 +47,6 @@ 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.entry.BottomSheetState
import com.tangem.features.markets.impl.R
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListLazyColumn
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListSortByBottomSheet

View file

@ -124,6 +124,7 @@ dependencies {
implementation(projects.features.tokenRecieve.api)
implementation(projects.features.yieldSupply.api)
implementation(projects.features.tangempay.details.api)
implementation(projects.features.feed.api)
/** Common modules */
implementation(projects.common)

View file

@ -1,8 +1,9 @@
package com.tangem.feature.wallet.child.wallet
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.slot.childSlot
@ -13,6 +14,7 @@ import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.decompose.ComposableDialogComponent
@ -23,6 +25,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogCon
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
import com.tangem.feature.walletsettings.component.RenameWalletComponent
import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.feed.entry.components.FeedEntryComponent
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
import com.tangem.features.markets.entry.MarketsEntryComponent
import com.tangem.features.pushnotifications.api.PushNotificationsBottomSheetComponent
import com.tangem.features.pushnotifications.api.PushNotificationsParams
@ -37,16 +41,25 @@ import kotlinx.coroutines.launch
internal class WalletComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted navigate: (WalletRoute) -> Unit,
marketsEntryComponentFactory: MarketsEntryComponent.Factory,
feedEntryComponentFactory: FeedEntryComponent.Factory,
private val renameWalletComponentFactory: RenameWalletComponent.Factory,
private val marketsEntryComponentFactory: MarketsEntryComponent.Factory,
private val askBiometryComponentFactory: AskBiometryComponent.Factory,
private val pushNotificationsBottomSheetComponent: PushNotificationsBottomSheetComponent.Factory,
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
private val yieldSupplyDepositedWarningComponent: YieldSupplyDepositedWarningComponent.Factory,
private val feedFeatureToggle: FeedFeatureToggle,
) : ComposableContentComponent, AppComponentContext by appComponentContext {
private val model: WalletModel = getOrCreateModel()
private val feedEntryComponent by lazy {
feedEntryComponentFactory.create(child("feedEntryComponent"))
}
private val marketsEntryComponent by lazy {
marketsEntryComponentFactory.create(child("marketsEntryComponent"))
}
init {
lifecycle.subscribe(model.screenLifecycleProvider)
doOnResume { model.onResume() }
@ -114,15 +127,23 @@ internal class WalletComponent @AssistedInject constructor(
},
)
private val marketsEntryComponent = marketsEntryComponentFactory.create(child("marketsEntryComponent"))
@Composable
override fun Content(modifier: Modifier) {
val bottomSheetState = remember { mutableStateOf(BottomSheetState.COLLAPSED) }
var headerSize by remember { mutableStateOf(0.dp) }
val dialog by dialog.subscribeAsState()
WalletScreen(
state = model.uiState.collectAsStateWithLifecycle().value,
marketsEntryComponent = marketsEntryComponent,
bottomSheetContent = {
BottomSheetContent(
bottomSheetState = bottomSheetState,
onHeaderSizeChange = { headerSize = it },
modifier = modifier,
)
},
bottomSheetHeaderHeightProvider = { headerSize },
onBottomSheetStateChange = { bottomSheetState.value = it },
)
when (val dialog = dialog.child?.instance) {
@ -132,6 +153,27 @@ internal class WalletComponent @AssistedInject constructor(
}
}
@Composable
private fun BottomSheetContent(
bottomSheetState: State<BottomSheetState>,
onHeaderSizeChange: (Dp) -> Unit,
modifier: Modifier = Modifier,
) {
if (feedFeatureToggle.isFeedEnabled) {
feedEntryComponent.BottomSheetContent(
bottomSheetState = bottomSheetState,
onHeaderSizeChange = onHeaderSizeChange,
modifier = modifier,
)
} else {
marketsEntryComponent.BottomSheetContent(
bottomSheetState = bottomSheetState,
onHeaderSizeChange = onHeaderSizeChange,
modifier = modifier,
)
}
}
@AssistedFactory
interface Factory {
fun create(appComponentContext: AppComponentContext, navigate: (WalletRoute) -> Unit): WalletComponent

View file

@ -42,6 +42,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
import com.tangem.features.tangempay.TangemPayFeatureToggles
@ -99,6 +100,7 @@ internal class WalletModel @Inject constructor(
private val accountsFeatureToggles: AccountsFeatureToggles,
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
private val trackingContextProxy: TrackingContextProxy,
private val feedFeatureToggle: FeedFeatureToggle,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
) : Model() {
@ -121,6 +123,7 @@ internal class WalletModel @Inject constructor(
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpenedLegacy())
}
updateMarketToggle()
suggestToOpenMarkets()
maybeMigrateNames()
@ -143,6 +146,12 @@ internal class WalletModel @Inject constructor(
}
}
private fun updateMarketToggle() {
stateHolder.update {
it.copy(isNewMarketEnabled = feedFeatureToggle.isFeedEnabled)
}
}
private fun updateYieldSupplyApy() {
if (yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled) {
modelScope.launch(dispatchers.default) {

View file

@ -223,6 +223,7 @@ internal object WalletScreenPreviewData {
isHidingMode = false,
showMarketsOnboarding = false,
onDismissMarketsOnboarding = {},
isNewMarketEnabled = false,
)
internal val accountScreenState =

View file

@ -110,6 +110,7 @@ internal class WalletStateController @Inject constructor() {
isHidingMode = false,
showMarketsOnboarding = false,
onDismissMarketsOnboarding = {},
isNewMarketEnabled = false,
)
}
}

View file

@ -13,5 +13,6 @@ internal data class WalletScreenState(
val event: StateEvent<WalletEvent>,
val isHidingMode: Boolean,
val showMarketsOnboarding: Boolean,
val isNewMarketEnabled: Boolean,
val onDismissMarketsOnboarding: () -> Unit,
)

View file

@ -51,6 +51,7 @@ import com.tangem.common.ui.expressStatus.expressTransactionsItems
import com.tangem.core.ui.components.atoms.Hand
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer
import com.tangem.core.ui.components.rememberIsKeyboardVisible
import com.tangem.core.ui.components.sheetscaffold.*
@ -87,15 +88,18 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.TangemPa
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.VisaTxDetailsBottomSheet
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balancesAndLimitsBlock
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
import com.tangem.features.markets.entry.BottomSheetState
import com.tangem.features.markets.entry.MarketsEntryComponent
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
@Composable
internal fun WalletScreen(state: WalletScreenState, marketsEntryComponent: MarketsEntryComponent) {
internal fun WalletScreen(
state: WalletScreenState,
bottomSheetContent: @Composable (() -> Unit),
bottomSheetHeaderHeightProvider: () -> Dp,
onBottomSheetStateChange: (BottomSheetState) -> Unit,
) {
// It means that screen is still initializing
if (state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX) return
@ -116,8 +120,10 @@ internal fun WalletScreen(state: WalletScreenState, marketsEntryComponent: Marke
snackbarHostState = snackbarHostState,
isAutoScroll = isAutoScroll,
onAutoScrollReset = { isAutoScroll.value = false },
marketsEntryComponent = marketsEntryComponent,
bottomSheetContent = bottomSheetContent,
alertConfig = alertConfig,
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
onBottomSheetStateChange = onBottomSheetStateChange,
)
WalletEventEffect(
@ -136,9 +142,11 @@ private fun WalletContent(
walletsListState: LazyListState,
snackbarHostState: SnackbarHostState,
isAutoScroll: State<Boolean>,
marketsEntryComponent: MarketsEntryComponent,
alertConfig: WalletAlertState?,
onAutoScrollReset: () -> Unit,
bottomSheetHeaderHeightProvider: () -> Dp,
onBottomSheetStateChange: (BottomSheetState) -> Unit,
bottomSheetContent: @Composable (() -> Unit),
) {
/*
* Don't pass key to remember, because it will brake scroll animation.
@ -286,25 +294,15 @@ private fun WalletContent(
)
}
val bottomSheetState = remember { mutableStateOf(BottomSheetState.COLLAPSED) }
var headerSize by remember { mutableStateOf(0.dp) }
BaseScaffoldWithMarkets(
state = state,
listState = listState,
selectedWallet = selectedWallet,
snackbarHostState = snackbarHostState,
bottomSheetHeaderHeightProvider = { headerSize },
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
alertConfig = alertConfig,
onBottomSheetStateChange = { bottomSheetState.value = it },
bottomSheetContent = {
marketsEntryComponent.BottomSheetContent(
bottomSheetState = bottomSheetState,
onHeaderSizeChange = { headerSize = it },
modifier = Modifier,
)
},
onBottomSheetStateChange = onBottomSheetStateChange,
bottomSheetContent = bottomSheetContent,
content = scaffoldContent,
)
}
@ -394,7 +392,11 @@ private inline fun BaseScaffoldWithMarkets(
val maxHeight = LocalWindowSize.current.height
val coroutineScope = rememberCoroutineScope()
val backgroundPrimary = TangemTheme.colors.background.primary
val background = if (state.isNewMarketEnabled) {
TangemTheme.colors.background.tertiary
} else {
TangemTheme.colors.background.primary
}
val showMarketsHint by remember {
derivedStateOf {
@ -407,7 +409,7 @@ private inline fun BaseScaffoldWithMarkets(
}
CompositionLocalProvider(
LocalMainBottomSheetColor provides remember { mutableStateOf(backgroundPrimary) },
LocalMainBottomSheetColor provides remember { mutableStateOf(background) },
) {
val backgroundColor = LocalMainBottomSheetColor.current
var isSearchFieldFocused by remember { mutableStateOf(false) }
@ -798,16 +800,11 @@ private fun WalletScreen_Preview(@PreviewParameter(WalletScreenPreviewProvider::
TangemThemePreview {
WalletScreen(
state = data,
marketsEntryComponent = object : MarketsEntryComponent {
@Composable
override fun BottomSheetContent(
bottomSheetState: State<BottomSheetState>,
onHeaderSizeChange: (Dp) -> Unit,
modifier: Modifier,
) {
Text("Markets Content")
}
bottomSheetContent = {
Text("Markets Content")
},
bottomSheetHeaderHeightProvider = { 10.dp },
onBottomSheetStateChange = {},
)
}
}