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

View file

@ -22,6 +22,7 @@ dependencies {
implementation(projects.domain.markets.models)
implementation(projects.domain.onramp.models)
implementation(projects.domain.appCurrency.models)
implementation(projects.domain.nft.models)
/* Libs - Other */
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.markets.TokenMarketParams
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.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
@ -288,4 +289,10 @@ sealed class AppRoute(val path: String) : Route {
data class NFTReceive(
val userWalletId: UserWalletId,
) : 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.kotlin.coroutines)
implementation(projects.domain.core)
implementation(projects.domain.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.tokens.model.Network
import kotlinx.serialization.Serializable
@Serializable
data class NFTAsset(
val id: Identifier,
val collectionId: NFTCollection.Identifier,
@ -18,29 +20,44 @@ data class NFTAsset(
val source: StatusSource,
) {
@Serializable
data class Media(
val mimetype: String?,
val url: String,
)
@Serializable
data class Rarity(
val rank: String,
val label: String,
)
@Serializable
data class Trait(
val name: String,
val value: String,
)
@Serializable
sealed class Identifier {
abstract val stringValue: String
@Serializable
data class EVM(
val tokenId: 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.tokens.model.Network
import kotlinx.serialization.Serializable
@Serializable
data class NFTCollection(
val id: Identifier,
val network: Network,
@ -12,19 +14,33 @@ data class NFTCollection(
val count: Int,
val assets: Assets,
) {
@Serializable
sealed class Assets {
@Serializable
data object Empty : Assets()
@Serializable
data object Loading : Assets()
@Serializable
data object Failed : Assets()
@Serializable
data class Value(
val items: List<NFTAsset>,
val source: StatusSource,
) : Assets()
}
@Serializable
sealed class Identifier {
@Serializable
data class EVM(val tokenAddress: String) : Identifier()
@Serializable
data class TON(val contractAddress: String?) : Identifier()
@Serializable
data object Unknown : Identifier()
}
}

View file

@ -1,25 +1,31 @@
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 {
abstract val assetId: NFTAsset.Identifier
@Serializable
data class Empty(
override val assetId: NFTAsset.Identifier,
) : NFTSalePrice()
@Serializable
data class Loading(
override val assetId: NFTAsset.Identifier,
) : NFTSalePrice()
@Serializable
data class Error(
override val assetId: NFTAsset.Identifier,
) : NFTSalePrice()
@Serializable
data class Value(
override val assetId: NFTAsset.Identifier,
val value: BigDecimal,
val value: SerializedBigDecimal,
val symbol: String,
) : NFTSalePrice()
}

View file

@ -12,6 +12,7 @@ dependencies {
/* Project - Domain */
implementation(projects.domain.models)
implementation(projects.domain.nft.models)
implementation(projects.domain.wallets.models)
/* 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.model.NFTCollectionsModel
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.details.DefaultNFTDetailsComponent
import com.tangem.features.nft.details.model.NFTDetailsModel
import com.tangem.features.nft.receive.DefaultNFTReceiveComponent
import com.tangem.features.nft.receive.model.NFTReceiveModel
import dagger.Binds
@ -50,4 +53,13 @@ internal interface NFTFeatureModuleBinds {
@IntoMap
@ClassKey(NFTReceiveModel::class)
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)
}