Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-02 14:20:37 +05:00
parent 7a45703d68
commit c1d4bf79cb
15 changed files with 188 additions and 6 deletions

View file

@ -13,6 +13,7 @@ import com.tangem.features.managetokens.component.ManageTokensComponent
import com.tangem.features.managetokens.component.ManageTokensSource import com.tangem.features.managetokens.component.ManageTokensSource
import com.tangem.features.markets.details.MarketsTokenDetailsComponent import com.tangem.features.markets.details.MarketsTokenDetailsComponent
import com.tangem.features.nft.component.NFTCollectionsComponent 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.NFTReceiveComponent
import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent
import com.tangem.features.onramp.component.* import com.tangem.features.onramp.component.*
@ -84,6 +85,7 @@ internal class ChildFactory @Inject constructor(
private val redesignedWalletConnectComponentFactory: RedisegnedWalletConnectComponent.Factory, private val redesignedWalletConnectComponentFactory: RedisegnedWalletConnectComponent.Factory,
private val nftCollectionsComponentFactory: NFTCollectionsComponent.Factory, private val nftCollectionsComponentFactory: NFTCollectionsComponent.Factory,
private val nftReceiveComponentFactory: NFTReceiveComponent.Factory, private val nftReceiveComponentFactory: NFTReceiveComponent.Factory,
private val nftDetailsComponentFactory: NFTDetailsComponent.Factory,
private val testerRouter: TesterRouter, private val testerRouter: TesterRouter,
private val routingFeatureToggles: RoutingFeatureToggles, private val routingFeatureToggles: RoutingFeatureToggles,
private val walletConnectFeatureToggles: WalletConnectFeatureToggles, private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
@ -415,6 +417,12 @@ internal class ChildFactory @Inject constructor(
params = NFTReceiveComponent.Params(userWalletId = route.userWalletId), params = NFTReceiveComponent.Params(userWalletId = route.userWalletId),
componentFactory = nftReceiveComponentFactory, componentFactory = nftReceiveComponentFactory,
) )
is AppRoute.NFTDetails ->
createComponentChild(
context = context,
params = NFTDetailsComponent.Params(userWalletId = route.userWalletId, nftAsset = route.nftAsset),
componentFactory = nftDetailsComponentFactory,
)
is AppRoute.OnboardingNote, is AppRoute.OnboardingNote,
is AppRoute.SaveWallet, is AppRoute.SaveWallet,
is AppRoute.OnboardingOther, is AppRoute.OnboardingOther,
@ -761,6 +769,12 @@ internal class ChildFactory @Inject constructor(
params = NFTReceiveComponent.Params(userWalletId = route.userWalletId), params = NFTReceiveComponent.Params(userWalletId = route.userWalletId),
componentFactory = nftReceiveComponentFactory, componentFactory = nftReceiveComponentFactory,
) )
is AppRoute.NFTDetails ->
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = NFTDetailsComponent.Params(userWalletId = route.userWalletId, nftAsset = route.nftAsset),
componentFactory = nftDetailsComponentFactory,
)
} }
// endregion // endregion
} }

View file

@ -22,6 +22,7 @@ dependencies {
implementation(projects.domain.markets.models) implementation(projects.domain.markets.models)
implementation(projects.domain.onramp.models) implementation(projects.domain.onramp.models)
implementation(projects.domain.appCurrency.models) implementation(projects.domain.appCurrency.models)
implementation(projects.domain.nft.models)
/* Libs - Other */ /* Libs - Other */
api(deps.kotlin.serialization) api(deps.kotlin.serialization)

View file

@ -8,6 +8,7 @@ import com.tangem.core.decompose.navigation.Route
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
@ -288,4 +289,10 @@ sealed class AppRoute(val path: String) : Route {
data class NFTReceive( data class NFTReceive(
val userWalletId: UserWalletId, val userWalletId: UserWalletId,
) : AppRoute(path = "/nft_receive/${userWalletId.stringValue}") ) : AppRoute(path = "/nft_receive/${userWalletId.stringValue}")
@Serializable
data class NFTDetails(
val userWalletId: UserWalletId,
val nftAsset: NFTAsset,
) : AppRoute(path = "/nft_details/${userWalletId.stringValue}/${nftAsset.collectionId}/${nftAsset.id.stringValue}")
} }

1
domain/networks/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -8,7 +8,10 @@ dependencies {
implementation(deps.arrow.core) implementation(deps.arrow.core)
implementation(deps.kotlin.coroutines) implementation(deps.kotlin.coroutines)
implementation(projects.domain.core)
implementation(projects.domain.models) implementation(projects.domain.models)
implementation(projects.domain.tokens.models) implementation(projects.domain.tokens.models)
/* Utils */
implementation(deps.kotlin.serialization)
} }

View file

@ -2,7 +2,9 @@ package com.tangem.domain.nft.models
import com.tangem.domain.models.StatusSource import com.tangem.domain.models.StatusSource
import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.Network
import kotlinx.serialization.Serializable
@Serializable
data class NFTAsset( data class NFTAsset(
val id: Identifier, val id: Identifier,
val collectionId: NFTCollection.Identifier, val collectionId: NFTCollection.Identifier,
@ -18,29 +20,44 @@ data class NFTAsset(
val source: StatusSource, val source: StatusSource,
) { ) {
@Serializable
data class Media( data class Media(
val mimetype: String?, val mimetype: String?,
val url: String, val url: String,
) )
@Serializable
data class Rarity( data class Rarity(
val rank: String, val rank: String,
val label: String, val label: String,
) )
@Serializable
data class Trait( data class Trait(
val name: String, val name: String,
val value: String, val value: String,
) )
@Serializable
sealed class Identifier { sealed class Identifier {
abstract val stringValue: String
@Serializable
data class EVM( data class EVM(
val tokenId: String, val tokenId: String,
val tokenAddress: String, val tokenAddress: String,
) : Identifier() ) : Identifier() {
override val stringValue: String = "${tokenAddress}_$tokenId"
}
data class TON(val tokenAddress: String) : Identifier() @Serializable
data class TON(val tokenAddress: String) : Identifier() {
override val stringValue: String = tokenAddress
}
data object Unknown : Identifier() @Serializable
data object Unknown : Identifier() {
override val stringValue: String = ""
}
} }
} }

View file

@ -2,7 +2,9 @@ package com.tangem.domain.nft.models
import com.tangem.domain.models.StatusSource import com.tangem.domain.models.StatusSource
import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.Network
import kotlinx.serialization.Serializable
@Serializable
data class NFTCollection( data class NFTCollection(
val id: Identifier, val id: Identifier,
val network: Network, val network: Network,
@ -12,19 +14,33 @@ data class NFTCollection(
val count: Int, val count: Int,
val assets: Assets, val assets: Assets,
) { ) {
@Serializable
sealed class Assets { sealed class Assets {
@Serializable
data object Empty : Assets() data object Empty : Assets()
@Serializable
data object Loading : Assets() data object Loading : Assets()
@Serializable
data object Failed : Assets() data object Failed : Assets()
@Serializable
data class Value( data class Value(
val items: List<NFTAsset>, val items: List<NFTAsset>,
val source: StatusSource, val source: StatusSource,
) : Assets() ) : Assets()
} }
@Serializable
sealed class Identifier { sealed class Identifier {
@Serializable
data class EVM(val tokenAddress: String) : Identifier() data class EVM(val tokenAddress: String) : Identifier()
@Serializable
data class TON(val contractAddress: String?) : Identifier() data class TON(val contractAddress: String?) : Identifier()
@Serializable
data object Unknown : Identifier() data object Unknown : Identifier()
} }
} }

View file

@ -1,25 +1,31 @@
package com.tangem.domain.nft.models package com.tangem.domain.nft.models
import java.math.BigDecimal import com.tangem.domain.core.serialization.SerializedBigDecimal
import kotlinx.serialization.Serializable
@Serializable
sealed class NFTSalePrice { sealed class NFTSalePrice {
abstract val assetId: NFTAsset.Identifier abstract val assetId: NFTAsset.Identifier
@Serializable
data class Empty( data class Empty(
override val assetId: NFTAsset.Identifier, override val assetId: NFTAsset.Identifier,
) : NFTSalePrice() ) : NFTSalePrice()
@Serializable
data class Loading( data class Loading(
override val assetId: NFTAsset.Identifier, override val assetId: NFTAsset.Identifier,
) : NFTSalePrice() ) : NFTSalePrice()
@Serializable
data class Error( data class Error(
override val assetId: NFTAsset.Identifier, override val assetId: NFTAsset.Identifier,
) : NFTSalePrice() ) : NFTSalePrice()
@Serializable
data class Value( data class Value(
override val assetId: NFTAsset.Identifier, override val assetId: NFTAsset.Identifier,
val value: BigDecimal, val value: SerializedBigDecimal,
val symbol: String, val symbol: String,
) : NFTSalePrice() ) : NFTSalePrice()
} }

View file

@ -12,6 +12,7 @@ dependencies {
/* Project - Domain */ /* Project - Domain */
implementation(projects.domain.models) implementation(projects.domain.models)
implementation(projects.domain.nft.models)
implementation(projects.domain.wallets.models) implementation(projects.domain.wallets.models)
/* Project - Core */ /* Project - Core */

View file

@ -0,0 +1,16 @@
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,
)
interface Factory : ComponentFactory<Params, NFTDetailsComponent>
}

View file

@ -5,7 +5,10 @@ import com.tangem.core.decompose.model.Model
import com.tangem.features.nft.collections.DefaultNFTCollectionsComponent import com.tangem.features.nft.collections.DefaultNFTCollectionsComponent
import com.tangem.features.nft.collections.model.NFTCollectionsModel import com.tangem.features.nft.collections.model.NFTCollectionsModel
import com.tangem.features.nft.component.NFTCollectionsComponent 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.NFTReceiveComponent
import com.tangem.features.nft.details.DefaultNFTDetailsComponent
import com.tangem.features.nft.details.model.NFTDetailsModel
import com.tangem.features.nft.receive.DefaultNFTReceiveComponent import com.tangem.features.nft.receive.DefaultNFTReceiveComponent
import com.tangem.features.nft.receive.model.NFTReceiveModel import com.tangem.features.nft.receive.model.NFTReceiveModel
import dagger.Binds import dagger.Binds
@ -50,4 +53,13 @@ internal interface NFTFeatureModuleBinds {
@IntoMap @IntoMap
@ClassKey(NFTReceiveModel::class) @ClassKey(NFTReceiveModel::class)
fun bindNFTReceiveModel(model: NFTReceiveModel): Model fun bindNFTReceiveModel(model: NFTReceiveModel): Model
@Binds
@Singleton
fun bindNFTDetailsComponentFactory(impl: DefaultNFTDetailsComponent.Factory): NFTDetailsComponent.Factory
@Binds
@IntoMap
@ClassKey(NFTDetailsModel::class)
fun bindNFTDetailsModel(model: NFTDetailsModel): Model
} }

View file

@ -0,0 +1,37 @@
package com.tangem.features.nft.details
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.features.nft.component.NFTDetailsComponent
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(
@Assisted context: AppComponentContext,
@Assisted private val params: NFTDetailsComponent.Params,
) : NFTDetailsComponent, AppComponentContext by context {
private val model: NFTDetailsModel = getOrCreateModel(params)
@Composable
override fun Content(modifier: Modifier) {
val state by model.state.collectAsStateWithLifecycle()
NFTDetails(state, modifier)
}
@AssistedFactory
interface Factory : NFTDetailsComponent.Factory {
override fun create(
context: AppComponentContext,
params: NFTDetailsComponent.Params,
): DefaultNFTDetailsComponent
}
}

View file

@ -0,0 +1,5 @@
package com.tangem.features.nft.details.entity
internal data class NFTDetailsUM(
val onBackClick: () -> Unit,
)

View file

@ -0,0 +1,34 @@
package com.tangem.features.nft.details.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.features.nft.component.NFTDetailsComponent
import com.tangem.features.nft.details.entity.NFTDetailsUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
import javax.inject.Inject
@ModelScoped
internal class NFTDetailsModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
paramsContainer: ParamsContainer,
) : Model() {
val state: StateFlow<NFTDetailsUM> get() = _state
private val _state = MutableStateFlow(
value = NFTDetailsUM(
onBackClick = ::navigateBack,
),
)
@Suppress("UnusedPrivateMember")
private val params: NFTDetailsComponent.Params = paramsContainer.require()
private fun navigateBack() {
router.pop()
}
}

View file

@ -0,0 +1,12 @@
package com.tangem.features.nft.details.ui
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.features.nft.details.entity.NFTDetailsUM
@Suppress("UnusedPrivateMember")
@Composable
internal fun NFTDetails(state: NFTDetailsUM, modifier: Modifier = Modifier) {
BackHandler(onBack = state.onBackClick)
}