Updated on 2026-08-14

This commit is contained in:
Tangem 2025-11-13 20:04:45 +01:00
parent f7b10f3ed5
commit f4978462fb
18 changed files with 503 additions and 0 deletions

View file

@ -0,0 +1,91 @@
package com.tangem.features.feed.components
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.popWhile
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.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 dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@Stable
internal class DefaultFeedEntryComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
private val feedEntryChildFactory: FeedEntryChildFactory,
) : FeedEntryComponent, AppComponentContext by context {
private val stackNavigation = StackNavigation<FeedEntryChildFactory.Child>()
private val innerRouter = InnerRouter<FeedEntryChildFactory.Child>(
stackNavigation = stackNavigation,
popCallback = { onChildBack() },
)
private val stack: Value<ChildStack<FeedEntryChildFactory.Child, Any>> = childStack(
key = "main",
source = stackNavigation,
serializer = FeedEntryChildFactory.Child.serializer(),
initialConfiguration = FeedEntryChildFactory.Child.Feed,
handleBackButton = false,
childFactory = { configuration, factoryContext ->
feedEntryChildFactory.createChild(
child = configuration,
appComponentContext = childByContext(
componentContext = factoryContext,
router = innerRouter,
),
onTokenClick = ::marketsListTokenSelected,
)
},
)
@Composable
override fun BottomSheetContent(
bottomSheetState: State<BottomSheetState>,
onHeaderSizeChange: (Dp) -> Unit,
modifier: Modifier,
) {
bottomSheetState // TODO will be continued in next tasks.
}
private fun marketsListTokenSelected(token: TokenMarketParams, appCurrency: AppCurrency) {
innerRouter.push(
route = FeedEntryChildFactory.Child.TokenDetails(
params = DefaultMarketsTokenDetailsComponent.Params(
token = token,
appCurrency = appCurrency,
shouldShowPortfolio = true,
analyticsParams = DefaultMarketsTokenDetailsComponent.AnalyticsParams(
blockchain = null,
source = "Market",
),
),
),
)
}
private fun onChildBack() {
if (stack.value.active.configuration !is FeedEntryChildFactory.Child.Feed) {
stackNavigation.popWhile { it != FeedEntryChildFactory.Child.Feed }
}
}
@AssistedFactory
interface Factory : FeedEntryComponent.Factory {
override fun create(context: AppComponentContext): DefaultFeedEntryComponent
}
}

View file

@ -0,0 +1,77 @@
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.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.features.feed.components.feed.DefaultFeedComponent
import com.tangem.features.feed.components.market.details.DefaultMarketsTokenDetailsComponent
import com.tangem.features.feed.components.market.list.DefaultMarketsTokenListComponent
import com.tangem.features.feed.components.news.details.DefaultNewsDetailsComponent
import com.tangem.features.feed.components.news.list.DefaultNewsListComponent
import kotlinx.serialization.Serializable
internal class FeedEntryChildFactory {
@Serializable
@Immutable
sealed interface Child : Route {
@Serializable
@Immutable
data object Feed : Child
@Serializable
@Immutable
data object TokenList : Child
@Serializable
@Immutable
data class TokenDetails(val params: DefaultMarketsTokenDetailsComponent.Params) : Child
@Serializable
@Immutable
data object NewsList : Child
@Serializable
@Immutable
data object NewsDetails : Child
}
fun createChild(
child: Child,
appComponentContext: AppComponentContext,
onTokenClick: (TokenMarketParams, AppCurrency) -> Unit,
): Any {
return when (child) {
is Child.TokenDetails -> {
DefaultMarketsTokenDetailsComponent(
appComponentContext = appComponentContext,
params = child.params,
)
}
is Child.TokenList -> {
DefaultMarketsTokenListComponent(
appComponentContext = appComponentContext,
onTokenClick = onTokenClick,
)
}
Child.NewsDetails -> {
DefaultNewsDetailsComponent(
appComponentContext = appComponentContext,
)
}
Child.NewsList -> {
DefaultNewsListComponent(
appComponentContext = appComponentContext,
)
}
Child.Feed -> {
DefaultFeedComponent(
appComponentContext = appComponentContext,
)
}
}
}
}

View file

@ -0,0 +1,23 @@
package com.tangem.features.feed.components.feed
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableModularContentComponent
internal class DefaultFeedComponent(
appComponentContext: AppComponentContext,
) : ComposableModularContentComponent, AppComponentContext by appComponentContext {
@Composable
override fun Title() {
}
@Composable
override fun Content(modifier: Modifier) {
}
@Composable
override fun Footer() {
}
}

View file

@ -0,0 +1,41 @@
package com.tangem.features.feed.components.market.details
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableModularContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
import kotlinx.serialization.Serializable
internal class DefaultMarketsTokenDetailsComponent(
appComponentContext: AppComponentContext,
val params: Params,
) : ComposableModularContentComponent, AppComponentContext by appComponentContext {
@Composable
override fun Title() {
}
@Composable
override fun Content(modifier: Modifier) {
}
@Composable
override fun Footer() {
}
@Serializable
data class Params(
val token: TokenMarketParams,
val appCurrency: AppCurrency,
val shouldShowPortfolio: Boolean,
val analyticsParams: AnalyticsParams?,
)
@Serializable
data class AnalyticsParams(
val blockchain: String?,
val source: String,
)
}

View file

@ -0,0 +1,27 @@
package com.tangem.features.feed.components.market.list
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableModularContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
@Suppress("UnusedPrivateProperty") // TODO will be remove in next PR
internal class DefaultMarketsTokenListComponent(
appComponentContext: AppComponentContext,
private val onTokenClick: ((TokenMarketParams, AppCurrency) -> Unit)? = null,
) : ComposableModularContentComponent, AppComponentContext by appComponentContext {
@Composable
override fun Title() {
}
@Composable
override fun Content(modifier: Modifier) {
}
@Composable
override fun Footer() {
}
}

View file

@ -0,0 +1,23 @@
package com.tangem.features.feed.components.news.details
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableModularContentComponent
internal class DefaultNewsDetailsComponent(
appComponentContext: AppComponentContext,
) : ComposableModularContentComponent, AppComponentContext by appComponentContext {
@Composable
override fun Title() {
}
@Composable
override fun Content(modifier: Modifier) {
}
@Composable
override fun Footer() {
}
}

View file

@ -0,0 +1,23 @@
package com.tangem.features.feed.components.news.list
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableModularContentComponent
internal class DefaultNewsListComponent(
appComponentContext: AppComponentContext,
) : ComposableModularContentComponent, AppComponentContext by appComponentContext {
@Composable
override fun Title() {
}
@Composable
override fun Content(modifier: Modifier) {
}
@Composable
override fun Footer() {
}
}

View file

@ -0,0 +1,23 @@
package com.tangem.features.feed.di
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
import com.tangem.features.feed.featuretoggle.DefaultFeedFeatureToggle
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object FeedFeatureToggleModule {
@Provides
@Singleton
fun provideFeedFeatureToggle(featureTogglesManager: FeatureTogglesManager): FeedFeatureToggle {
return DefaultFeedFeatureToggle(
featureTogglesManager = featureTogglesManager,
)
}
}

View file

@ -0,0 +1,12 @@
package com.tangem.features.feed.featuretoggle
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
internal class DefaultFeedFeatureToggle(
private val featureTogglesManager: FeatureTogglesManager,
) : FeedFeatureToggle {
override val isFeedEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled("FEED_ENABLED")
}