Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-14 12:12:25 +05:00
parent fc4c7ef9ab
commit cb04ddc533
21 changed files with 325 additions and 269 deletions

View file

@ -12,10 +12,7 @@ import com.tangem.features.disclaimer.api.components.DisclaimerComponent
import com.tangem.features.managetokens.component.ManageTokensComponent
import com.tangem.features.managetokens.component.ManageTokensSource
import com.tangem.features.markets.details.MarketsTokenDetailsComponent
import com.tangem.features.nft.component.NFTCollectionsComponent
import com.tangem.features.nft.component.NFTDetailsComponent
import com.tangem.features.nft.component.NFTReceiveComponent
import com.tangem.features.nft.component.NFTAssetTraitsComponent
import com.tangem.features.nft.component.*
import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent
import com.tangem.features.onramp.component.*
import com.tangem.features.pushnotifications.api.PushNotificationsComponent
@ -85,10 +82,7 @@ internal class ChildFactory @Inject constructor(
private val sendComponentFactoryV2: com.tangem.features.send.v2.api.SendComponent.Factory,
private val sendFeatureToggles: SendFeatureToggles,
private val redesignedWalletConnectComponentFactory: RedisegnedWalletConnectComponent.Factory,
private val nftCollectionsComponentFactory: NFTCollectionsComponent.Factory,
private val nftReceiveComponentFactory: NFTReceiveComponent.Factory,
private val nftDetailsComponentFactory: NFTDetailsComponent.Factory,
private val nftAssetTraitsComponentFactory: NFTAssetTraitsComponent.Factory,
private val nftComponentFactory: NFTComponent.Factory,
private val nftSendComponentFactory: NFTSendComponent.Factory,
private val testerRouter: TesterRouter,
private val routingFeatureToggles: RoutingFeatureToggles,
@ -409,33 +403,11 @@ internal class ChildFactory @Inject constructor(
componentFactory = walletComponentFactory,
)
}
is AppRoute.NFTCollections ->
is AppRoute.NFT ->
createComponentChild(
context = context,
params = NFTCollectionsComponent.Params(userWalletId = route.userWalletId),
componentFactory = nftCollectionsComponentFactory,
)
is AppRoute.NFTReceive ->
createComponentChild(
context = context,
params = NFTReceiveComponent.Params(userWalletId = route.userWalletId),
componentFactory = nftReceiveComponentFactory,
)
is AppRoute.NFTDetails ->
createComponentChild(
context = context,
params = NFTDetailsComponent.Params(
userWalletId = route.userWalletId,
nftAsset = route.nftAsset,
nftCollectionName = route.collectionName,
),
componentFactory = nftDetailsComponentFactory,
)
is AppRoute.NFTAssetTraits ->
createComponentChild(
context = context,
params = NFTAssetTraitsComponent.Params(nftAsset = route.nftAsset),
componentFactory = nftAssetTraitsComponentFactory,
params = NFTComponent.Params(userWalletId = route.userWalletId),
componentFactory = nftComponentFactory,
)
is AppRoute.NFTSend -> {
createComponentChild(
@ -782,33 +754,11 @@ internal class ChildFactory @Inject constructor(
componentFactory = storiesComponentFactory,
)
}
is AppRoute.NFTCollections ->
is AppRoute.NFT ->
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = NFTCollectionsComponent.Params(userWalletId = route.userWalletId),
componentFactory = nftCollectionsComponentFactory,
)
is AppRoute.NFTReceive ->
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = NFTReceiveComponent.Params(userWalletId = route.userWalletId),
componentFactory = nftReceiveComponentFactory,
)
is AppRoute.NFTDetails ->
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = NFTDetailsComponent.Params(
userWalletId = route.userWalletId,
nftAsset = route.nftAsset,
nftCollectionName = route.collectionName,
),
componentFactory = nftDetailsComponentFactory,
)
is AppRoute.NFTAssetTraits ->
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = NFTAssetTraitsComponent.Params(nftAsset = route.nftAsset),
componentFactory = nftAssetTraitsComponentFactory,
params = NFTComponent.Params(userWalletId = route.userWalletId),
componentFactory = nftComponentFactory,
)
is AppRoute.NFTSend -> {
route.asComponentChild(

View file

@ -281,26 +281,9 @@ sealed class AppRoute(val path: String) : Route {
) : AppRoute(path = "/stories$storyId")
@Serializable
data class NFTCollections(
data class NFT(
val userWalletId: UserWalletId,
) : AppRoute(path = "/nft_collections/${userWalletId.stringValue}")
@Serializable
data class NFTReceive(
val userWalletId: UserWalletId,
) : AppRoute(path = "/nft_receive/${userWalletId.stringValue}")
@Serializable
data class NFTDetails(
val userWalletId: UserWalletId,
val nftAsset: NFTAsset,
val collectionName: String,
) : AppRoute(path = "/nft_details/${userWalletId.stringValue}/${nftAsset.collectionId}/${nftAsset.id.stringValue}")
@Serializable
data class NFTAssetTraits(
val nftAsset: NFTAsset,
) : AppRoute(path = "/nft_traits/${nftAsset.collectionId}/${nftAsset.id.stringValue}")
) : AppRoute(path = "/nft/${userWalletId.stringValue}")
@Serializable
data class NFTSend(

View file

@ -1,14 +0,0 @@
package com.tangem.features.nft.component
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.nft.models.NFTAsset
interface NFTAssetTraitsComponent : ComposableContentComponent {
data class Params(
val nftAsset: NFTAsset,
)
interface Factory : ComponentFactory<Params, NFTAssetTraitsComponent>
}

View file

@ -1,14 +0,0 @@
package com.tangem.features.nft.component
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
interface NFTCollectionsComponent : ComposableContentComponent {
data class Params(
val userWalletId: UserWalletId,
)
interface Factory : ComponentFactory<Params, NFTCollectionsComponent>
}

View file

@ -4,11 +4,11 @@ import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
interface NFTReceiveComponent : ComposableContentComponent {
interface NFTComponent : ComposableContentComponent {
data class Params(
val userWalletId: UserWalletId,
)
interface Factory : ComponentFactory<Params, NFTReceiveComponent>
interface Factory : ComponentFactory<Params, NFTComponent>
}

View file

@ -1,17 +0,0 @@
package com.tangem.features.nft.component
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.wallets.models.UserWalletId
interface NFTDetailsComponent : ComposableContentComponent {
data class Params(
val userWalletId: UserWalletId,
val nftAsset: NFTAsset,
val nftCollectionName: String,
)
interface Factory : ComponentFactory<Params, NFTDetailsComponent>
}

View file

@ -6,17 +6,18 @@ 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.ComposableContentComponent
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.nft.collections.model.NFTCollectionsModel
import com.tangem.features.nft.collections.ui.NFTCollections
import com.tangem.features.nft.component.NFTCollectionsComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultNFTCollectionsComponent @AssistedInject constructor(
internal class NFTCollectionsComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted private val params: NFTCollectionsComponent.Params,
) : NFTCollectionsComponent, AppComponentContext by context {
@Assisted private val params: Params,
) : ComposableContentComponent, AppComponentContext by context {
private val model: NFTCollectionsModel = getOrCreateModel(params)
@ -27,11 +28,10 @@ internal class DefaultNFTCollectionsComponent @AssistedInject constructor(
NFTCollections(state, modifier)
}
@AssistedFactory
interface Factory : NFTCollectionsComponent.Factory {
override fun create(
context: AppComponentContext,
params: NFTCollectionsComponent.Params,
): DefaultNFTCollectionsComponent
}
data class Params(
val userWalletId: UserWalletId,
val onBackClick: () -> Unit,
val onAssetClick: (asset: NFTAsset, collectionName: String) -> Unit,
val onReceiveClick: () -> Unit,
)
}

View file

@ -1,24 +1,21 @@
package com.tangem.features.nft.collections.model
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.components.fields.InputManager
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.nft.FetchNFTCollectionAssetsUseCase
import com.tangem.domain.nft.GetNFTCollectionsUseCase
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.nft.models.NFTCollection
import com.tangem.features.nft.collections.NFTCollectionsComponent
import com.tangem.features.nft.collections.entity.NFTCollectionsStateUM
import com.tangem.features.nft.collections.entity.NFTCollectionsUM
import com.tangem.features.nft.collections.entity.transformer.ChangeCollectionExpandedStateTransformer
import com.tangem.features.nft.collections.entity.transformer.ToggleSearchBarTransformer
import com.tangem.features.nft.collections.entity.transformer.UpdateDataStateTransformer
import com.tangem.features.nft.collections.entity.transformer.UpdateSearchQueryTransformer
import com.tangem.features.nft.component.NFTCollectionsComponent
import com.tangem.features.nft.impl.R
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
@ -29,23 +26,22 @@ import javax.inject.Inject
@ModelScoped
internal class NFTCollectionsModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val searchManager: InputManager,
private val getNFTCollectionsUseCase: GetNFTCollectionsUseCase,
private val fetchNFTCollectionAssetsUseCase: FetchNFTCollectionAssetsUseCase,
paramsContainer: ParamsContainer,
) : Model() {
val state: StateFlow<NFTCollectionsStateUM> get() = _state
private val params: NFTCollectionsComponent.Params = paramsContainer.require()
private val _state = MutableStateFlow(
value = NFTCollectionsStateUM(
onBackClick = ::navigateBack,
content = NFTCollectionsUM.Loading(::onReceiveClick),
onBackClick = params.onBackClick,
content = NFTCollectionsUM.Loading(params.onBackClick),
),
)
private val params: NFTCollectionsComponent.Params = paramsContainer.require()
val state: StateFlow<NFTCollectionsStateUM> get() = _state
init {
subscribeToNFTCollections()
@ -60,11 +56,15 @@ internal class NFTCollectionsModel @Inject constructor(
UpdateDataStateTransformer(
nftCollections = nftCollections,
searchQuery = query,
onReceiveClick = ::onReceiveClick,
onReceiveClick = {
params.onReceiveClick()
},
onRetryClick = ::onRetryClick,
onExpandCollectionClick = ::onExpandCollectionClick,
onRetryAssetsClick = ::onRetryAssetsClick,
onAssetClick = ::onAssetClick,
onAssetClick = { asset, collectionName ->
params.onAssetClick(asset, collectionName)
},
initialSearchBarFactory = ::getInitialSearchBar,
).transform(it)
}
@ -115,28 +115,6 @@ internal class NFTCollectionsModel @Inject constructor(
// TODO refresh all
}
private fun onAssetClick(asset: NFTAsset, collectionName: String) {
router.push(
AppRoute.NFTDetails(
userWalletId = params.userWalletId,
nftAsset = asset,
collectionName = collectionName,
),
)
}
private fun onReceiveClick() {
router.push(
AppRoute.NFTReceive(
userWalletId = params.userWalletId,
),
)
}
private fun navigateBack() {
router.pop()
}
private fun loadCollectionAssets(collection: NFTCollection) {
modelScope.launch {
fetchNFTCollectionAssetsUseCase(

View file

@ -0,0 +1,172 @@
package com.tangem.features.nft.common
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.value.ObserveLifecycleMode
import com.arkivanov.decompose.value.subscribe
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.decompose.ComposableContentComponent
import com.tangem.features.nft.collections.NFTCollectionsComponent
import com.tangem.features.nft.common.ui.NFTContent
import com.tangem.features.nft.component.NFTComponent
import com.tangem.features.nft.details.NFTDetailsComponent
import com.tangem.features.nft.receive.NFTReceiveComponent
import com.tangem.features.nft.traits.NFTAssetTraitsComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
internal class DefaultNFTComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted private val params: NFTComponent.Params,
) : NFTComponent, AppComponentContext by appComponentContext {
private val stackNavigation = StackNavigation<NFTRoute>()
private val innerRouter = InnerRouter<NFTRoute>(
stackNavigation = stackNavigation,
popCallback = { onChildBack() },
)
private val initialRoute: NFTRoute = NFTRoute.Collections(params.userWalletId)
private val currentRoute = MutableStateFlow(initialRoute)
private val childStack = childStack(
key = "sendInnerStack",
source = stackNavigation,
serializer = null,
initialConfiguration = initialRoute,
handleBackButton = true,
childFactory = { configuration, factoryContext ->
createChild(
configuration,
childByContext(
componentContext = factoryContext,
router = innerRouter,
),
)
},
)
init {
childStack.subscribe(
lifecycle = lifecycle,
mode = ObserveLifecycleMode.CREATE_DESTROY,
) { stack ->
componentScope.launch {
currentRoute.emit(stack.active.configuration)
}
}
}
@Composable
override fun Content(modifier: Modifier) {
val stackState by childStack.subscribeAsState()
BackHandler(onBack = ::onChildBack)
NFTContent(
stackState = stackState,
)
}
private fun createChild(route: NFTRoute, factoryContext: AppComponentContext) = when (route) {
is NFTRoute.Collections -> getCollectionsComponent(factoryContext, route)
is NFTRoute.Receive -> getReceiveComponent(factoryContext, route)
is NFTRoute.Details -> getDetailsComponent(factoryContext, route)
is NFTRoute.AssetTraits -> getAssetTraitsComponent(factoryContext, route)
}
private fun getCollectionsComponent(
factoryContext: AppComponentContext,
route: NFTRoute.Collections,
): ComposableContentComponent = NFTCollectionsComponent(
context = factoryContext,
params = NFTCollectionsComponent.Params(
userWalletId = route.userWalletId,
onBackClick = ::onChildBack,
onReceiveClick = {
innerRouter.push(
NFTRoute.Receive(
userWalletId = route.userWalletId,
),
)
},
onAssetClick = { asset, collectionName ->
innerRouter.push(
NFTRoute.Details(
userWalletId = route.userWalletId,
nftAsset = asset,
collectionName = collectionName,
),
)
},
),
)
private fun getReceiveComponent(
factoryContext: AppComponentContext,
route: NFTRoute.Receive,
): ComposableContentComponent = NFTReceiveComponent(
context = factoryContext,
params = NFTReceiveComponent.Params(
userWalletId = route.userWalletId,
onBackClick = ::onChildBack,
),
)
private fun getDetailsComponent(
factoryContext: AppComponentContext,
route: NFTRoute.Details,
): ComposableContentComponent = NFTDetailsComponent(
context = factoryContext,
params = NFTDetailsComponent.Params(
userWalletId = route.userWalletId,
nftAsset = route.nftAsset,
nftCollectionName = route.collectionName,
onBackClick = ::onChildBack,
onAllTraitsClick = {
innerRouter.push(
NFTRoute.AssetTraits(
nftAsset = route.nftAsset,
),
)
},
),
)
private fun getAssetTraitsComponent(
factoryContext: AppComponentContext,
route: NFTRoute.AssetTraits,
): ComposableContentComponent = NFTAssetTraitsComponent(
context = factoryContext,
params = NFTAssetTraitsComponent.Params(
nftAsset = route.nftAsset,
onBackClick = ::onChildBack,
),
)
private fun onChildBack() {
val isEmptyStack = childStack.value.backStack.isEmpty()
if (isEmptyStack) {
router.pop()
} else {
stackNavigation.pop()
}
}
@AssistedFactory
interface Factory : NFTComponent.Factory {
override fun create(context: AppComponentContext, params: NFTComponent.Params): DefaultNFTComponent
}
}

View file

@ -0,0 +1,31 @@
package com.tangem.features.nft.common
import com.tangem.core.decompose.navigation.Route
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.serialization.Serializable
internal sealed class NFTRoute : Route {
@Serializable
data class Collections(
val userWalletId: UserWalletId,
) : NFTRoute()
@Serializable
data class Receive(
val userWalletId: UserWalletId,
) : NFTRoute()
@Serializable
data class Details(
val userWalletId: UserWalletId,
val nftAsset: NFTAsset,
val collectionName: String,
) : NFTRoute()
@Serializable
data class AssetTraits(
val nftAsset: NFTAsset,
) : NFTRoute()
}

View file

@ -0,0 +1,37 @@
package com.tangem.features.nft.common.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.decompose.ComposableContentComponent
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.nft.common.NFTRoute
@Composable
internal fun NFTContent(stackState: ChildStack<NFTRoute, ComposableContentComponent>) {
Column(
modifier = Modifier.Companion
.background(color = TangemTheme.colors.background.tertiary)
.fillMaxSize()
.imePadding()
.systemBarsPadding(),
horizontalAlignment = Alignment.Companion.CenterHorizontally,
) {
Children(
stack = stackState,
animation = stackAnimation(slide()),
modifier = Modifier.weight(1f),
) {
it.instance.Content(Modifier.weight(1f))
}
}
}

View file

@ -6,17 +6,18 @@ 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.features.nft.component.NFTDetailsComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.nft.details.model.NFTDetailsModel
import com.tangem.features.nft.details.ui.NFTDetails
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultNFTDetailsComponent @AssistedInject constructor(
internal class NFTDetailsComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted private val params: NFTDetailsComponent.Params,
) : NFTDetailsComponent, AppComponentContext by context {
@Assisted private val params: Params,
) : ComposableContentComponent, AppComponentContext by context {
private val model: NFTDetailsModel = getOrCreateModel(params)
@ -27,11 +28,11 @@ internal class DefaultNFTDetailsComponent @AssistedInject constructor(
NFTDetails(state, modifier)
}
@AssistedFactory
interface Factory : NFTDetailsComponent.Factory {
override fun create(
context: AppComponentContext,
params: NFTDetailsComponent.Params,
): DefaultNFTDetailsComponent
}
data class Params(
val userWalletId: UserWalletId,
val nftAsset: NFTAsset,
val nftCollectionName: String,
val onBackClick: () -> Unit,
val onAllTraitsClick: () -> Unit,
)
}

View file

@ -5,7 +5,7 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.features.nft.component.NFTDetailsComponent
import com.tangem.features.nft.details.NFTDetailsComponent
import com.tangem.features.nft.details.entity.NFTAssetUM
import com.tangem.features.nft.details.entity.NFTDetailsUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -35,9 +35,9 @@ internal class NFTDetailsModel @Inject constructor(
traits = persistentListOf(),
baseInfoItems = persistentListOf(),
),
onBackClick = ::navigateBack,
onBackClick = params.onBackClick,
onReadMoreClick = ::onReadMoreClick,
onSeeAllTraitsClick = ::onSeeAllTraitsClick,
onSeeAllTraitsClick = params.onAllTraitsClick,
onExploreClick = ::onExploreClick,
onSendClick = ::onSendClick,
bottomSheetConfig = null,
@ -48,14 +48,6 @@ internal class NFTDetailsModel @Inject constructor(
// TODO implement
}
private fun onSeeAllTraitsClick() {
router.push(
AppRoute.NFTAssetTraits(
nftAsset = params.nftAsset,
),
)
}
private fun onExploreClick() {
// TODO implement
}
@ -69,8 +61,4 @@ internal class NFTDetailsModel @Inject constructor(
),
)
}
private fun navigateBack() {
router.pop()
}
}

View file

@ -4,19 +4,12 @@ import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.decompose.model.Model
import com.tangem.features.nft.DefaultNFTFeatureToggles
import com.tangem.features.nft.NFTFeatureToggles
import com.tangem.features.nft.collections.DefaultNFTCollectionsComponent
import com.tangem.features.nft.collections.model.NFTCollectionsModel
import com.tangem.features.nft.component.NFTCollectionsComponent
import com.tangem.features.nft.component.NFTDetailsBlockComponent
import com.tangem.features.nft.component.NFTDetailsComponent
import com.tangem.features.nft.component.NFTReceiveComponent
import com.tangem.features.nft.component.NFTAssetTraitsComponent
import com.tangem.features.nft.details.DefaultNFTDetailsComponent
import com.tangem.features.nft.common.DefaultNFTComponent
import com.tangem.features.nft.component.*
import com.tangem.features.nft.details.block.DefaultNFTDetailsBlockComponent
import com.tangem.features.nft.details.model.NFTDetailsModel
import com.tangem.features.nft.receive.DefaultNFTReceiveComponent
import com.tangem.features.nft.receive.model.NFTReceiveModel
import com.tangem.features.nft.traits.DefaultNFTAssetTraitsComponent
import com.tangem.features.nft.traits.model.NFTAssetTraitsModel
import dagger.Binds
import dagger.Module
@ -43,28 +36,18 @@ internal object NFTFeatureModule {
internal interface NFTFeatureModuleBinds {
@Binds
@Singleton
fun bindNFTCollectionsComponentFactory(
impl: DefaultNFTCollectionsComponent.Factory,
): NFTCollectionsComponent.Factory
fun bindNFTComponentFactory(impl: DefaultNFTComponent.Factory): NFTComponent.Factory
@Binds
@IntoMap
@ClassKey(NFTCollectionsModel::class)
fun bindNFTCollectionsModel(model: NFTCollectionsModel): Model
@Binds
@Singleton
fun bindNFTReceiveComponentFactory(impl: DefaultNFTReceiveComponent.Factory): NFTReceiveComponent.Factory
@Binds
@IntoMap
@ClassKey(NFTReceiveModel::class)
fun bindNFTReceiveModel(model: NFTReceiveModel): Model
@Binds
@Singleton
fun bindNFTDetailsComponentFactory(impl: DefaultNFTDetailsComponent.Factory): NFTDetailsComponent.Factory
@Binds
@IntoMap
@ClassKey(NFTDetailsModel::class)
@ -76,10 +59,6 @@ internal interface NFTFeatureModuleBinds {
impl: DefaultNFTDetailsBlockComponent.Factory,
): NFTDetailsBlockComponent.Factory
@Binds
@Singleton
fun bindNFTTraitsComponentFactory(impl: DefaultNFTAssetTraitsComponent.Factory): NFTAssetTraitsComponent.Factory
@Binds
@IntoMap
@ClassKey(NFTAssetTraitsModel::class)

View file

@ -6,17 +6,17 @@ 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.features.nft.component.NFTReceiveComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.nft.receive.model.NFTReceiveModel
import com.tangem.features.nft.receive.ui.NFTReceive
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultNFTReceiveComponent @AssistedInject constructor(
internal class NFTReceiveComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted private val params: NFTReceiveComponent.Params,
) : NFTReceiveComponent, AppComponentContext by context {
@Assisted private val params: Params,
) : ComposableContentComponent, AppComponentContext by context {
private val model: NFTReceiveModel = getOrCreateModel(params)
@ -27,11 +27,8 @@ internal class DefaultNFTReceiveComponent @AssistedInject constructor(
NFTReceive(state, modifier)
}
@AssistedFactory
interface Factory : NFTReceiveComponent.Factory {
override fun create(
context: AppComponentContext,
params: NFTReceiveComponent.Params,
): DefaultNFTReceiveComponent
}
data class Params(
val userWalletId: UserWalletId,
val onBackClick: () -> Unit,
)
}

View file

@ -3,7 +3,6 @@ package com.tangem.features.nft.receive.model
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.components.fields.InputManager
@ -14,8 +13,8 @@ import com.tangem.domain.nft.GetNFTAvailableNetworksUseCase
import com.tangem.domain.nft.GetNFTNetworkStatusUseCase
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkStatus
import com.tangem.features.nft.component.NFTReceiveComponent
import com.tangem.features.nft.impl.R
import com.tangem.features.nft.receive.NFTReceiveComponent
import com.tangem.features.nft.receive.entity.NFTReceiveUM
import com.tangem.features.nft.receive.entity.transformer.ShowReceiveBottomSheetTransformer
import com.tangem.features.nft.receive.entity.transformer.ToggleSearchBarTransformer
@ -31,7 +30,6 @@ import javax.inject.Inject
@ModelScoped
internal class NFTReceiveModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val searchManager: InputManager,
private val getNFTAvailableNetworksUseCase: GetNFTAvailableNetworksUseCase,
private val filterNFTAvailableNetworksUseCase: FilterNFTAvailableNetworksUseCase,
@ -41,18 +39,18 @@ internal class NFTReceiveModel @Inject constructor(
paramsContainer: ParamsContainer,
) : Model() {
val state: StateFlow<NFTReceiveUM> get() = _state
private val params: NFTReceiveComponent.Params = paramsContainer.require()
private val _state = MutableStateFlow(
value = NFTReceiveUM(
onBackClick = ::navigateBack,
onBackClick = params.onBackClick,
search = getInitialSearchBar(),
networks = NFTReceiveUM.Networks.Content(persistentListOf()),
bottomSheetConfig = null,
),
)
private val params: NFTReceiveComponent.Params = paramsContainer.require()
val state: StateFlow<NFTReceiveUM> get() = _state
init {
subscribeToNFTAvailableNetworks()
@ -140,8 +138,4 @@ internal class NFTReceiveModel @Inject constructor(
private fun onShareClick(text: String) {
shareManager.shareText(text = text)
}
private fun navigateBack() {
router.pop()
}
}

View file

@ -6,17 +6,17 @@ 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.features.nft.component.NFTAssetTraitsComponent
import com.tangem.features.nft.traits.ui.NFTAssetTraits
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.features.nft.traits.model.NFTAssetTraitsModel
import com.tangem.features.nft.traits.ui.NFTAssetTraits
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultNFTAssetTraitsComponent @AssistedInject constructor(
internal class NFTAssetTraitsComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted private val params: NFTAssetTraitsComponent.Params,
) : NFTAssetTraitsComponent, AppComponentContext by context {
@Assisted private val params: Params,
) : ComposableContentComponent, AppComponentContext by context {
private val model: NFTAssetTraitsModel = getOrCreateModel(params)
@ -27,11 +27,8 @@ internal class DefaultNFTAssetTraitsComponent @AssistedInject constructor(
NFTAssetTraits(state)
}
@AssistedFactory
interface Factory : NFTAssetTraitsComponent.Factory {
override fun create(
context: AppComponentContext,
params: NFTAssetTraitsComponent.Params,
): DefaultNFTAssetTraitsComponent
}
data class Params(
val nftAsset: NFTAsset,
val onBackClick: () -> Unit,
)
}

View file

@ -3,9 +3,8 @@ package com.tangem.features.nft.traits.model
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.features.nft.component.NFTAssetTraitsComponent
import com.tangem.features.nft.traits.NFTAssetTraitsComponent
import com.tangem.features.nft.traits.entity.NFTAssetTraitUM
import com.tangem.features.nft.traits.entity.NFTAssetTraitsUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -17,21 +16,20 @@ import javax.inject.Inject
@ModelScoped
internal class NFTAssetTraitsModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
paramsContainer: ParamsContainer,
) : Model() {
private val params: NFTAssetTraitsComponent.Params = paramsContainer.require()
val state: StateFlow<NFTAssetTraitsUM> get() = _state
private val _state = MutableStateFlow(
value = NFTAssetTraitsUM(
traits = params.nftAsset.transform(),
onBackClick = ::navigateBack,
onBackClick = { params.onBackClick() },
),
)
val state: StateFlow<NFTAssetTraitsUM> get() = _state
private fun NFTAsset.transform(): ImmutableList<NFTAssetTraitUM> = this
.traits
.mapIndexed { index, trait ->
@ -41,8 +39,4 @@ internal class NFTAssetTraitsModel @Inject constructor(
value = trait.value,
)
}.toPersistentList()
private fun navigateBack() {
router.pop()
}
}

View file

@ -240,6 +240,6 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
}
override fun onNFTClick(userWalletId: UserWalletId) {
router.openNFTCollectionsScreen(userWalletId)
router.openNFT(userWalletId)
}
}

View file

@ -116,7 +116,7 @@ internal class DefaultWalletRouter @Inject constructor(
reduxStateHolder.dispatchDialogShow(StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN, onTryAgain))
}
override fun openNFTCollectionsScreen(userWalletId: UserWalletId) {
router.push(AppRoute.NFTCollections(userWalletId))
override fun openNFT(userWalletId: UserWalletId) {
router.push(AppRoute.NFT(userWalletId))
}
}

View file

@ -58,5 +58,5 @@ internal interface InnerWalletRouter {
fun openScanFailedDialog(onTryAgain: () -> Unit)
/** Open NFT collections screen */
fun openNFTCollectionsScreen(userWalletId: UserWalletId)
fun openNFT(userWalletId: UserWalletId)
}