Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-16 12:49:16 +05:00
parent e3b1480e32
commit ad4e7e78e9
7 changed files with 96 additions and 24 deletions

View file

@ -3,10 +3,10 @@ package com.tangem.tap.routing.utils
import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.feature.usedesk.api.UsedeskComponent
import com.tangem.feature.qrscanning.QrScanningComponent import com.tangem.feature.qrscanning.QrScanningComponent
import com.tangem.feature.referral.api.ReferralComponent import com.tangem.feature.referral.api.ReferralComponent
import com.tangem.feature.stories.api.StoriesComponent import com.tangem.feature.stories.api.StoriesComponent
import com.tangem.feature.usedesk.api.UsedeskComponent
import com.tangem.feature.walletsettings.component.WalletSettingsComponent import com.tangem.feature.walletsettings.component.WalletSettingsComponent
import com.tangem.features.details.component.DetailsComponent import com.tangem.features.details.component.DetailsComponent
import com.tangem.features.disclaimer.api.components.DisclaimerComponent import com.tangem.features.disclaimer.api.components.DisclaimerComponent
@ -49,7 +49,7 @@ internal class ChildFactory @Inject constructor(
private val disclaimerComponentFactory: DisclaimerComponent.Factory, private val disclaimerComponentFactory: DisclaimerComponent.Factory,
private val manageTokensComponentFactory: ManageTokensComponent.Factory, private val manageTokensComponentFactory: ManageTokensComponent.Factory,
private val marketsTokenDetailsComponentFactory: MarketsTokenDetailsComponent.Factory, private val marketsTokenDetailsComponentFactory: MarketsTokenDetailsComponent.Factory,
private val marketsTokenListComponentFactory: MarketsTokenListComponent.Factory, private val marketsTokenListComponentFactory: MarketsTokenListComponent.FactoryScreen,
private val onrampComponentFactory: OnrampComponent.Factory, private val onrampComponentFactory: OnrampComponent.Factory,
private val onrampSuccessComponentFactory: OnrampSuccessComponent.Factory, private val onrampSuccessComponentFactory: OnrampSuccessComponent.Factory,
private val buyCryptoComponentFactory: BuyCryptoComponent.Factory, private val buyCryptoComponentFactory: BuyCryptoComponent.Factory,

View file

@ -5,8 +5,11 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent 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 com.tangem.features.markets.entry.BottomSheetState
@Stable @Stable
@ -19,5 +22,13 @@ interface MarketsTokenListComponent : ComposableContentComponent {
modifier: Modifier, modifier: Modifier,
) )
interface Factory : ComponentFactory<Unit, MarketsTokenListComponent> interface FactoryScreen : ComponentFactory<Unit, MarketsTokenListComponent>
interface FactoryBottomSheet {
fun create(
context: AppComponentContext,
params: Unit,
onTokenClick: ((TokenMarketParams, AppCurrency) -> Unit)?,
): MarketsTokenListComponent
}
} }

View file

@ -5,6 +5,7 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import com.arkivanov.decompose.ExperimentalDecomposeApi
import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.StackNavigation
@ -14,6 +15,9 @@ import com.arkivanov.decompose.value.Value
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.navigation.inner.InnerRouter 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.markets.details.MarketsTokenDetailsComponent
import com.tangem.features.markets.entry.BottomSheetState import com.tangem.features.markets.entry.BottomSheetState
import com.tangem.features.markets.entry.MarketsEntryComponent import com.tangem.features.markets.entry.MarketsEntryComponent
import com.tangem.features.markets.entry.impl.MarketsEntryChildFactory.Child import com.tangem.features.markets.entry.impl.MarketsEntryChildFactory.Child
@ -48,6 +52,7 @@ internal class DefaultMarketsEntryComponent @AssistedInject constructor(
componentContext = factoryContext, componentContext = factoryContext,
router = innerRouter, router = innerRouter,
), ),
onTokenClick = ::marketsListTokenSelected,
) )
}, },
) )
@ -67,6 +72,23 @@ internal class DefaultMarketsEntryComponent @AssistedInject constructor(
) )
} }
@OptIn(ExperimentalDecomposeApi::class)
private fun marketsListTokenSelected(token: TokenMarketParams, appCurrency: AppCurrency) {
innerRouter.push(
route = Child.TokenDetails(
params = MarketsTokenDetailsComponent.Params(
token = token,
appCurrency = appCurrency,
showPortfolio = true,
analyticsParams = MarketsTokenDetailsComponent.AnalyticsParams(
blockchain = null,
source = "Market",
),
),
),
)
}
private fun onChildBack() { private fun onChildBack() {
if (stack.value.active.configuration !is Child.TokenList) { if (stack.value.active.configuration !is Child.TokenList) {
stackNavigation.popWhile { it != Child.TokenList } stackNavigation.popWhile { it != Child.TokenList }

View file

@ -3,13 +3,15 @@ package com.tangem.features.markets.entry.impl
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.navigation.Route import com.tangem.core.decompose.navigation.Route
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.details.MarketsTokenDetailsComponent
import com.tangem.features.markets.tokenlist.MarketsTokenListComponent import com.tangem.features.markets.tokenlist.MarketsTokenListComponent
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import javax.inject.Inject import javax.inject.Inject
internal class MarketsEntryChildFactory @Inject constructor( internal class MarketsEntryChildFactory @Inject constructor(
private val tokenListComponentFactory: MarketsTokenListComponent.Factory, private val tokenListComponentFactory: MarketsTokenListComponent.FactoryBottomSheet,
private val tokenDetailsComponentFactory: MarketsTokenDetailsComponent.Factory, private val tokenDetailsComponentFactory: MarketsTokenDetailsComponent.Factory,
) { ) {
@ -26,7 +28,11 @@ internal class MarketsEntryChildFactory @Inject constructor(
data class TokenDetails(val params: MarketsTokenDetailsComponent.Params) : Child data class TokenDetails(val params: MarketsTokenDetailsComponent.Params) : Child
} }
fun createChild(child: Child, appComponentContext: AppComponentContext): Any { fun createChild(
child: Child,
appComponentContext: AppComponentContext,
onTokenClick: (TokenMarketParams, AppCurrency) -> Unit,
): Any {
return when (child) { return when (child) {
is Child.TokenDetails -> { is Child.TokenDetails -> {
tokenDetailsComponentFactory.create( tokenDetailsComponentFactory.create(
@ -38,6 +44,7 @@ internal class MarketsEntryChildFactory @Inject constructor(
tokenListComponentFactory.create( tokenListComponentFactory.create(
context = appComponentContext, context = appComponentContext,
params = Unit, params = Unit,
onTokenClick = onTokenClick,
) )
} }
} }

View file

@ -46,7 +46,7 @@ internal fun EntryBottomSheetContent(
modifier = modifier, modifier = modifier,
) )
} }
MarketsEntryChildFactory.Child.TokenList -> { is MarketsEntryChildFactory.Child.TokenList -> {
(it.instance as MarketsTokenListComponent).BottomSheetContent( (it.instance as MarketsTokenListComponent).BottomSheetContent(
bottomSheetState = bottomSheetState, bottomSheetState = bottomSheetState,
onHeaderSizeChange = onHeaderSizeChange, onHeaderSizeChange = onHeaderSizeChange,
@ -84,7 +84,7 @@ private fun BackgroundColorEffects(
animationSpec = tween(durationMillis = 500), animationSpec = tween(durationMillis = 500),
) )
} }
MarketsEntryChildFactory.Child.TokenList -> { is MarketsEntryChildFactory.Child.TokenList -> {
backgroundColor.animateTo( backgroundColor.animateTo(
primary, primary,
animationSpec = tween(durationMillis = 500), animationSpec = tween(durationMillis = 500),
@ -119,7 +119,7 @@ private fun BackgroundColorEffects(
is MarketsEntryChildFactory.Child.TokenDetails -> { is MarketsEntryChildFactory.Child.TokenDetails -> {
backgroundColor.snapTo(tertiary) backgroundColor.snapTo(tertiary)
} }
MarketsEntryChildFactory.Child.TokenList -> { is MarketsEntryChildFactory.Child.TokenList -> {
backgroundColor.snapTo(primary) backgroundColor.snapTo(primary)
} }
} }

View file

@ -18,6 +18,8 @@ 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.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.WindowInsetsZero 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.domain.markets.toSerializableParam
import com.tangem.features.markets.entry.BottomSheetState import com.tangem.features.markets.entry.BottomSheetState
import com.tangem.features.markets.tokenlist.MarketsTokenListComponent import com.tangem.features.markets.tokenlist.MarketsTokenListComponent
@ -31,9 +33,10 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@Suppress("UnusedPrivateMember") @Suppress("UnusedPrivateMember")
class DefaultMarketsTokenListComponent @AssistedInject constructor( internal class DefaultMarketsTokenListComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext, @Assisted appComponentContext: AppComponentContext,
@Assisted params: Unit, @Assisted params: Unit,
@Assisted onTokenClick: ((TokenMarketParams, AppCurrency) -> Unit)? = null,
) : AppComponentContext by appComponentContext, MarketsTokenListComponent { ) : AppComponentContext by appComponentContext, MarketsTokenListComponent {
private val model: MarketsListModel = getOrCreateModel() private val model: MarketsListModel = getOrCreateModel()
@ -41,17 +44,23 @@ class DefaultMarketsTokenListComponent @AssistedInject constructor(
init { init {
model.tokenSelected model.tokenSelected
.onEach { (token, appCurrency) -> .onEach { (token, appCurrency) ->
router.push( // If specific routing call back is provided, invoke it (for example inner routing in Bottom Sheet)
AppRoute.MarketsTokenDetails( // Otherwise navigate using app routing
token = token.toSerializableParam(), if (onTokenClick != null) {
appCurrency = appCurrency, onTokenClick(token.toSerializableParam(), appCurrency)
showPortfolio = true, } else {
analyticsParams = AnalyticsParams( router.push(
blockchain = null, AppRoute.MarketsTokenDetails(
source = "Market", token = token.toSerializableParam(),
appCurrency = appCurrency,
showPortfolio = true,
analyticsParams = AnalyticsParams(
blockchain = null,
source = "Market",
),
), ),
), )
) }
} }
.launchIn(componentScope) .launchIn(componentScope)
} }
@ -112,7 +121,11 @@ class DefaultMarketsTokenListComponent @AssistedInject constructor(
} }
@AssistedFactory @AssistedFactory
interface Factory : MarketsTokenListComponent.Factory { interface FactoryBottomSheet : MarketsTokenListComponent.FactoryBottomSheet {
override fun create(context: AppComponentContext, params: Unit): DefaultMarketsTokenListComponent override fun create(
context: AppComponentContext,
params: Unit,
onTokenClick: ((TokenMarketParams, AppCurrency) -> Unit)?,
): DefaultMarketsTokenListComponent
} }
} }

View file

@ -1,9 +1,11 @@
package com.tangem.features.markets.tokenlist.impl.di package com.tangem.features.markets.tokenlist.impl.di
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.features.markets.tokenlist.MarketsTokenListComponent import com.tangem.features.markets.tokenlist.MarketsTokenListComponent
import com.tangem.features.markets.tokenlist.impl.DefaultMarketsTokenListComponent import com.tangem.features.markets.tokenlist.impl.DefaultMarketsTokenListComponent
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton import javax.inject.Singleton
@ -14,7 +16,24 @@ internal interface ComponentModule {
@Binds @Binds
@Singleton @Singleton
fun bindMarketsTokenListComponent( fun bindMarketsTokenListBottomSheetComponent(
factory: DefaultMarketsTokenListComponent.Factory, factory: DefaultMarketsTokenListComponent.FactoryBottomSheet,
): MarketsTokenListComponent.Factory ): MarketsTokenListComponent.FactoryBottomSheet
}
@Module
@InstallIn(SingletonComponent::class)
internal class ComponentProvideModule {
@Provides
@Singleton
fun providesMarketsTokenListScreenComponent(
factory: DefaultMarketsTokenListComponent.FactoryBottomSheet,
): MarketsTokenListComponent.FactoryScreen {
return object : MarketsTokenListComponent.FactoryScreen {
override fun create(context: AppComponentContext, params: Unit): MarketsTokenListComponent {
return factory.create(context, params, null)
}
}
}
} }