Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-25 13:19:26 +05:00
parent e00fd4599e
commit 46a4a93711
24 changed files with 693 additions and 43 deletions

View file

@ -0,0 +1,122 @@
package com.tangem.core.ui.components.chip
import android.content.res.Configuration
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.chip.entity.ChipUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@Composable
fun Chip(state: ChipUM, modifier: Modifier = Modifier) {
val backgroundColor by animateColorAsState(
targetValue = if (state.isSelected) {
TangemTheme.colors.button.primary
} else {
TangemTheme.colors.button.secondary
},
)
val textColor by animateColorAsState(
targetValue = if (state.isSelected) {
TangemTheme.colors.text.primary2
} else {
TangemTheme.colors.text.primary1
},
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = modifier
.clip(RoundedCornerShape(12.dp))
.background(color = backgroundColor)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(),
onClick = state.onClick,
)
.padding(PaddingValues(horizontal = 24.dp, vertical = 8.dp)),
) {
Text(
text = state.text.resolveReference(),
style = TangemTheme.typography.button,
color = textColor,
)
}
}
@OptIn(ExperimentalLayoutApi::class)
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun ChipPreview() {
TangemThemePreview {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier
.background(TangemTheme.colors.background.primary)
.padding(16.dp),
) {
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Chip(
state = ChipUM(
id = 0,
text = TextReference.Str("All News"),
isSelected = true,
onClick = {},
),
)
Chip(
state = ChipUM(
id = 1,
text = TextReference.Str("Regulation"),
isSelected = false,
onClick = {},
),
)
Chip(
state = ChipUM(
id = 2,
text = TextReference.Str("ETFs"),
isSelected = false,
onClick = {},
),
)
Chip(
state = ChipUM(
id = 3,
text = TextReference.Str("Institutions"),
isSelected = false,
onClick = {},
),
)
}
}
}
}

View file

@ -0,0 +1,10 @@
package com.tangem.core.ui.components.chip.entity
import com.tangem.core.ui.extensions.TextReference
data class ChipUM(
val id: Int,
val text: TextReference,
val isSelected: Boolean = false,
val onClick: () -> Unit,
)

View file

@ -168,7 +168,9 @@ private fun LabelPreview() {
TangemThemePreview { TangemThemePreview {
Column( Column(
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.padding(16.dp), modifier = Modifier
.background(TangemTheme.colors.background.primary)
.padding(16.dp),
) { ) {
FlowRow( FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),

View file

@ -9,6 +9,8 @@ android {
} }
dependencies { dependencies {
implementation(deps.compose.foundation)
/* Project - Core */ /* Project - Core */
implementation(projects.core.decompose) implementation(projects.core.decompose)
implementation(projects.core.ui) implementation(projects.core.ui)

View file

@ -1,11 +1,23 @@
package com.tangem.features.news.details.api package com.tangem.features.news.details.api
import androidx.compose.runtime.Composable
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.decompose.factory.ComponentFactory
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
interface NewsDetailsComponent : ComposableContentComponent { interface NewsDetailsComponent : ComposableContentComponent {
data class Params(val selectedArticleId: Int = 0) data class Params(val selectedArticleId: Int = 0)
@Composable
fun BottomSheetContent(
bottomSheetState: State<BottomSheetState>,
onHeaderSizeChange: (Dp) -> Unit,
modifier: Modifier,
)
interface Factory : ComponentFactory<Params, NewsDetailsComponent> interface Factory : ComponentFactory<Params, NewsDetailsComponent>
} }

View file

@ -1,11 +1,15 @@
package com.tangem.features.news.details.impl package com.tangem.features.news.details.impl
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.features.news.details.api.NewsDetailsComponent import com.tangem.features.news.details.api.NewsDetailsComponent
import com.tangem.features.news.details.impl.ui.NewsDetailsContent import com.tangem.features.news.details.impl.ui.NewsDetailsContent
import dagger.assisted.Assisted import dagger.assisted.Assisted
@ -19,16 +23,40 @@ internal class DefaultNewsDetailsComponent @AssistedInject constructor(
private val model: NewsDetailsModel = getOrCreateModel(params) private val model: NewsDetailsModel = getOrCreateModel(params)
@Composable
override fun BottomSheetContent(
bottomSheetState: State<BottomSheetState>,
onHeaderSizeChange: (Dp) -> Unit,
modifier: Modifier,
) {
val uiState by model.uiState.collectAsStateWithLifecycle()
val bsState by bottomSheetState
BackHandler(enabled = bsState == BottomSheetState.EXPANDED) {
navigateBack()
}
NewsDetailsContent(
state = uiState,
onBackClick = ::navigateBack,
modifier = modifier,
isBottomSheetMode = true,
)
}
@Composable @Composable
override fun Content(modifier: Modifier) { override fun Content(modifier: Modifier) {
val uiState by model.uiState.collectAsState() val uiState by model.uiState.collectAsStateWithLifecycle()
NewsDetailsContent( NewsDetailsContent(
state = uiState, state = uiState,
onBackClick = model::onBackClick, onBackClick = model::onBackClick,
modifier = modifier, modifier = modifier,
isBottomSheetMode = false,
) )
} }
private fun navigateBack() = router.pop()
@AssistedFactory @AssistedFactory
interface Factory : NewsDetailsComponent.Factory { interface Factory : NewsDetailsComponent.Factory {
override fun create( override fun create(

View file

@ -24,13 +24,19 @@ import com.tangem.core.ui.components.appbar.TangemTopAppBar
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.components.buttons.common.TangemButtonSize import com.tangem.core.ui.components.buttons.common.TangemButtonSize
import com.tangem.core.ui.components.pager.PagerIndicator import com.tangem.core.ui.components.pager.PagerIndicator
import com.tangem.core.ui.extensions.conditionalCompose
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.news.details.impl.MockArticlesFactory import com.tangem.features.news.details.impl.MockArticlesFactory
// TODO [REDACTED_TASK_KEY] make internal // TODO [REDACTED_TASK_KEY] make internal
@Composable @Composable
fun NewsDetailsContent(state: NewsDetailsUM, onBackClick: () -> Unit, modifier: Modifier = Modifier) { fun NewsDetailsContent(
state: NewsDetailsUM,
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
isBottomSheetMode: Boolean = false,
) {
val pagerState = rememberPagerState( val pagerState = rememberPagerState(
initialPage = state.selectedArticleIndex, initialPage = state.selectedArticleIndex,
pageCount = { state.articles.size }, pageCount = { state.articles.size },
@ -39,10 +45,9 @@ fun NewsDetailsContent(state: NewsDetailsUM, onBackClick: () -> Unit, modifier:
Column( Column(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.background(TangemTheme.colors.background.secondary) .background(TangemTheme.colors.background.tertiary)
.systemBarsPadding(), .conditionalCompose(isBottomSheetMode) { systemBarsPadding() },
) { ) {
Column {
TangemTopAppBar( TangemTopAppBar(
title = null, title = null,
startButton = TopAppBarButtonUM.Icon( startButton = TopAppBarButtonUM.Icon(
@ -86,7 +91,6 @@ fun NewsDetailsContent(state: NewsDetailsUM, onBackClick: () -> Unit, modifier:
} }
} }
} }
}
} }
@Suppress("LongMethod") @Suppress("LongMethod")

View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,20 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
id("configuration")
}
android {
namespace = "com.tangem.features.news.list.api"
}
dependencies {
implementation(deps.compose.foundation)
/* Project - Core */
implementation(projects.core.decompose)
implementation(projects.core.ui)
/* Compose */
implementation(deps.compose.runtime)
}

View file

@ -0,0 +1,23 @@
package com.tangem.features.news.list.api
import androidx.compose.runtime.Composable
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
interface NewsListComponent : ComposableContentComponent {
data class Params(val selectedFilter: String? = null)
@Composable
fun BottomSheetContent(
bottomSheetState: State<BottomSheetState>,
onHeaderSizeChange: (Dp) -> Unit,
modifier: Modifier,
)
interface Factory : ComponentFactory<Params, NewsListComponent>
}

View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,46 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.kapt)
alias(deps.plugins.hilt.android)
id("configuration")
}
android {
namespace = "com.tangem.features.news.list.impl"
}
dependencies {
/* AndroidX */
implementation(deps.lifecycle.compose)
implementation(deps.androidx.activity.compose)
/** Compose */
implementation(deps.compose.foundation)
implementation(deps.compose.ui)
implementation(deps.compose.ui.tooling)
implementation(deps.compose.material3)
/** Core modules */
implementation(projects.core.ui)
implementation(projects.core.utils)
implementation(projects.core.decompose)
implementation(projects.common.ui)
implementation(projects.common.routing)
/** Feature modules */
implementation(projects.features.news.newsList.api)
/** Domain modules */
implementation(projects.domain.models)
implementation(projects.domain.news)
/** Other dependencies */
implementation(deps.kotlin.immutable.collections)
implementation(deps.arrow.core)
implementation(deps.timber)
/** DI */
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
}

View file

@ -0,0 +1,62 @@
package com.tangem.features.news.list.impl
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
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.features.news.list.api.NewsListComponent
import com.tangem.features.news.list.impl.ui.NewsListContent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultNewsListComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted params: NewsListComponent.Params,
) : NewsListComponent, AppComponentContext by context {
private val model: NewsListModel = getOrCreateModel(params)
@Composable
override fun BottomSheetContent(
bottomSheetState: State<BottomSheetState>,
onHeaderSizeChange: (Dp) -> Unit,
modifier: Modifier,
) {
val uiState by model.uiState.collectAsStateWithLifecycle()
val bsState by bottomSheetState
BackHandler(enabled = bsState == BottomSheetState.EXPANDED) {
navigateBack()
}
NewsListContent(
state = uiState,
onBackClick = ::navigateBack,
modifier = modifier,
)
}
@Composable
override fun Content(modifier: Modifier) {
val uiState by model.uiState.collectAsStateWithLifecycle()
NewsListContent(
state = uiState,
onBackClick = model::onBackClick,
modifier = modifier,
)
}
private fun navigateBack() = router.pop()
@AssistedFactory
interface Factory : NewsListComponent.Factory {
override fun create(context: AppComponentContext, params: NewsListComponent.Params): DefaultNewsListComponent
}
}

View file

@ -0,0 +1,81 @@
package com.tangem.features.news.list.impl
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.components.chip.entity.ChipUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.news.usecase.GetNewsCategoriesUseCase
import com.tangem.features.news.list.impl.ui.NewsListUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
@ModelScoped
internal class NewsListModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val getNewsCategoriesUseCase: GetNewsCategoriesUseCase,
private val router: Router,
) : Model() {
val uiState: StateFlow<NewsListUM>
field = MutableStateFlow(
NewsListUM(
selectedCategoryId = 0,
filters = persistentListOf(),
articles = persistentListOf(),
onArticleClick = ::onArticleClick,
),
)
init {
modelScope.launch(dispatchers.default) {
val filterChips = getNewsCategoriesUseCase
.invoke()
.map { articleCategory ->
ChipUM(
id = articleCategory.id,
text = TextReference.Str(articleCategory.name),
isSelected = false,
onClick = {
onCategoryClick(articleCategory.id)
},
)
}
.toImmutableList()
uiState.update { currentState ->
currentState.copy(filters = filterChips)
}
}
}
fun onBackClick() {
router.pop()
}
private fun onArticleClick(articleId: Int) {
// TODO [REDACTED_TASK_KEY]
articleId
}
private fun onCategoryClick(categoryId: Int) {
uiState.update { currentState ->
currentState.copy(
selectedCategoryId = categoryId,
filters = updateFilterChips(categoryId),
)
}
}
private fun updateFilterChips(categoryId: Int): ImmutableList<ChipUM> {
return uiState.value.filters.map { chip ->
chip.copy(isSelected = chip.id == categoryId)
}.toImmutableList()
}
}

View file

@ -0,0 +1,18 @@
package com.tangem.features.news.list.impl.di
import com.tangem.features.news.list.api.NewsListComponent
import com.tangem.features.news.list.impl.DefaultNewsListComponent
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 NewsListModule {
@Binds
@Singleton
fun bindNewsListComponentFactory(factory: DefaultNewsListComponent.Factory): NewsListComponent.Factory
}

View file

@ -0,0 +1,152 @@
package com.tangem.features.news.list.impl.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
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.appbar.AppBarWithBackButton
import com.tangem.core.ui.components.block.TangemBlockCardColors
import com.tangem.core.ui.components.chip.Chip
import com.tangem.core.ui.components.chip.entity.ChipUM
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableSet
@Composable
internal fun NewsListContent(state: NewsListUM, onBackClick: () -> Unit, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxSize()
.background(color = TangemTheme.colors.background.tertiary),
) {
AppBarWithBackButton(
text = stringResourceSafe(R.string.common_news),
onBackClick = onBackClick,
)
LazyRow(
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
items(
items = state.filters,
key = { it.id },
) { filter ->
Chip(state = filter)
}
}
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
) {
items(
items = state.articles,
key = ArticleConfigUM::id,
) { article ->
ArticleCard(
articleConfigUM = article,
onArticleClick = { state.onArticleClick(article.id) },
modifier = Modifier
.fillMaxWidth()
.height(164.dp),
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
)
SpacerH(12.dp)
}
}
}
}
@Suppress("LongMethod")
@Preview(showBackground = true)
@Composable
private fun NewsListContentPreview() {
val tags = listOf(
LabelUM(TextReference.Str("Regulation")),
LabelUM(TextReference.Str("BTC")),
LabelUM(TextReference.Str("ETH")),
).toImmutableSet()
val articles = persistentListOf(
ArticleConfigUM(
id = 1,
title = "SEC delays decisions on ETH-staking ETFs and spot XRP/SOL funds",
score = 6.5f,
createdAt = TextReference.Str("1h ago"),
isTrending = false,
tags = tags,
isViewed = false,
),
ArticleConfigUM(
id = 2,
title = "Bitcoin ETFs log 4th straight day of inflows (+\$550M)",
score = 8.6f,
createdAt = TextReference.Str("8h ago"),
isTrending = false,
tags = tags,
isViewed = true,
),
ArticleConfigUM(
id = 3,
title = "Bitcoin reclaims ~\$115K amid macro prints and ETF optimism",
score = 7.8f,
createdAt = TextReference.Str("22 Jun, 11:30"),
isTrending = false,
tags = tags,
isViewed = false,
),
)
val filters = persistentListOf(
ChipUM(
id = 0,
text = TextReference.Str("All News"),
isSelected = true,
onClick = {},
),
ChipUM(
id = 1,
text = TextReference.Str("Regulation"),
isSelected = false,
onClick = {},
),
ChipUM(
id = 2,
text = TextReference.Str("ETFs"),
isSelected = false,
onClick = {},
),
ChipUM(
id = 3,
text = TextReference.Str("Institutions"),
isSelected = false,
onClick = {},
),
)
TangemThemePreview {
NewsListContent(
state = NewsListUM(
selectedCategoryId = 0,
filters = filters,
articles = articles,
onArticleClick = {},
),
onBackClick = {},
)
}
}

View file

@ -0,0 +1,14 @@
package com.tangem.features.news.list.impl.ui
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.news.ArticleConfigUM
import com.tangem.core.ui.components.chip.entity.ChipUM
import kotlinx.collections.immutable.ImmutableList
@Immutable
data class NewsListUM(
val selectedCategoryId: Int?,
val filters: ImmutableList<ChipUM>,
val articles: ImmutableList<ArticleConfigUM>,
val onArticleClick: (Int) -> Unit,
)

View file

@ -61,6 +61,8 @@ dependencies {
implementation(projects.features.pushNotifications.api) implementation(projects.features.pushNotifications.api)
implementation(projects.features.news.newsDetails.api) implementation(projects.features.news.newsDetails.api)
implementation(projects.features.news.newsDetails.impl) implementation(projects.features.news.newsDetails.impl)
implementation(projects.features.news.newsList.api)
implementation(projects.features.news.newsList.impl)
/* SDK */ /* SDK */
implementation(tangemDeps.blockchain) implementation(tangemDeps.blockchain)

View file

@ -14,6 +14,9 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.tangem.common.routing.AppRouter import com.tangem.common.routing.AppRouter
import com.tangem.core.navigation.finisher.AppFinisher import com.tangem.core.navigation.finisher.AppFinisher
import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeActivity import com.tangem.core.ui.screen.ComposeActivity
import com.tangem.feature.tester.presentation.accounts.ui.AccountsScreen import com.tangem.feature.tester.presentation.accounts.ui.AccountsScreen
@ -38,9 +41,11 @@ import com.tangem.feature.tester.presentation.testpush.viewmodel.TestPushViewMod
import com.tangem.feature.tester.presentation.news.ui.NewsScreen import com.tangem.feature.tester.presentation.news.ui.NewsScreen
import com.tangem.feature.tester.presentation.news.viewmodel.NewsViewModel import com.tangem.feature.tester.presentation.news.viewmodel.NewsViewModel
import com.tangem.features.news.details.impl.MockArticlesFactory import com.tangem.features.news.details.impl.MockArticlesFactory
import com.tangem.features.news.details.impl.ui.ArticleUM
import com.tangem.features.news.details.impl.ui.NewsDetailsContent import com.tangem.features.news.details.impl.ui.NewsDetailsContent
import com.tangem.features.news.details.impl.ui.NewsDetailsUM import com.tangem.features.news.details.impl.ui.NewsDetailsUM
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.persistentSetOf
import javax.inject.Inject import javax.inject.Inject
@ -190,6 +195,43 @@ internal class TesterActivity : ComposeActivity() {
onBackClick = { innerTesterRouter.back() }, onBackClick = { innerTesterRouter.back() },
) )
} }
composable(route = TesterScreen.NEWS_DETAILS_BOTTOM_SHEET.name) {
NewsDetailsBottomSheetTest(
onDismiss = { innerTesterRouter.back() },
)
}
}
}
@Composable
private fun NewsDetailsBottomSheetTest(onDismiss: () -> Unit) {
data class NewsDetailsBottomSheetContent(
val articles: ImmutableList<ArticleUM>,
) : TangemBottomSheetConfigContent
val content = NewsDetailsBottomSheetContent(
articles = MockArticlesFactory.createMockArticles(),
)
TangemBottomSheet<NewsDetailsBottomSheetContent>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = onDismiss,
content = content,
),
containerColor = TangemTheme.colors.background.tertiary,
) { sheetContent ->
NewsDetailsContent(
state = NewsDetailsUM(
articles = sheetContent.articles,
selectedArticleIndex = 0,
onLikeClick = { },
onShareClick = { },
),
onBackClick = onDismiss,
isBottomSheetMode = true,
)
} }
} }
} }

View file

@ -16,4 +16,5 @@ internal enum class TesterScreen {
ACCOUNTS, ACCOUNTS,
NEWS, NEWS,
NEWS_DETAILS, NEWS_DETAILS,
NEWS_DETAILS_BOTTOM_SHEET,
} }

View file

@ -12,5 +12,6 @@ data class NewsUM(
enum class ButtonUM(@StringRes val textResId: Int) { enum class ButtonUM(@StringRes val textResId: Int) {
NEWS_DETAILS(R.string.news_details), NEWS_DETAILS(R.string.news_details),
NEWS_DETAILS_BOTTOM_SHEET(R.string.news_details_bottom_sheet),
} }
} }

View file

@ -27,6 +27,7 @@ internal class NewsViewModel @Inject constructor() : ViewModel() {
onBackClick = ::onBackClick, onBackClick = ::onBackClick,
buttons = persistentSetOf( buttons = persistentSetOf(
NewsUM.ButtonUM.NEWS_DETAILS, NewsUM.ButtonUM.NEWS_DETAILS,
NewsUM.ButtonUM.NEWS_DETAILS_BOTTOM_SHEET,
), ),
onButtonClick = ::onButtonClick, onButtonClick = ::onButtonClick,
) )
@ -39,6 +40,7 @@ internal class NewsViewModel @Inject constructor() : ViewModel() {
private fun onButtonClick(button: NewsUM.ButtonUM) { private fun onButtonClick(button: NewsUM.ButtonUM) {
when (button) { when (button) {
NewsUM.ButtonUM.NEWS_DETAILS -> router?.open(TesterScreen.NEWS_DETAILS) NewsUM.ButtonUM.NEWS_DETAILS -> router?.open(TesterScreen.NEWS_DETAILS)
NewsUM.ButtonUM.NEWS_DETAILS_BOTTOM_SHEET -> router?.open(TesterScreen.NEWS_DETAILS_BOTTOM_SHEET)
} }
} }
} }

View file

@ -20,4 +20,5 @@
<string name="accounts" translatable="false">Accounts</string> <string name="accounts" translatable="false">Accounts</string>
<string name="news" translatable="false">News</string> <string name="news" translatable="false">News</string>
<string name="news_details" translatable="false">News details</string> <string name="news_details" translatable="false">News details</string>
<string name="news_details_bottom_sheet" translatable="false">News details (Bottom Sheet)</string>
</resources> </resources>

View file

@ -305,6 +305,9 @@ include(":features:feed:impl")
include(":features:news:news-details:api") include(":features:news:news-details:api")
include(":features:news:news-details:impl") include(":features:news:news-details:impl")
include(":features:news:news-list:api")
include(":features:news:news-list:impl")
// endregion Feature modules // endregion Feature modules
// region Domain modules // region Domain modules