Updated on 2026-08-14
This commit is contained in:
parent
4cd56a1588
commit
17ae97606e
13 changed files with 119 additions and 64 deletions
|
|
@ -23,6 +23,7 @@ dependencies {
|
|||
/** Project */
|
||||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.res)
|
||||
implementation(projects.libs.auth)
|
||||
implementation(projects.domain.appTheme.models)
|
||||
implementation(projects.domain.core)
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ internal class DefaultNFTRuntimeStore(
|
|||
)
|
||||
}
|
||||
?.filter { it.count > 0 }
|
||||
?.sortedBy { it.name },
|
||||
?.sortedBy { it.name?.lowercase() },
|
||||
source = this.source,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.datasource.local.nft.converter
|
||||
|
||||
import android.content.res.Resources
|
||||
import com.tangem.datasource.R
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.domain.nft.models.NFTCollection
|
||||
|
|
@ -7,14 +9,31 @@ import com.tangem.domain.tokens.model.Network
|
|||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.nft.models.NFTCollection as SdkNFTCollection
|
||||
|
||||
object NFTSdkCollectionConverter : Converter<Pair<Network, SdkNFTCollection>, NFTCollection> {
|
||||
class NFTSdkCollectionConverter(
|
||||
private val resources: Resources,
|
||||
) : Converter<Pair<Network, SdkNFTCollection>, NFTCollection> {
|
||||
override fun convert(value: Pair<Network, SdkNFTCollection>): NFTCollection {
|
||||
val (network, collection) = value
|
||||
val collectionId = NFTSdkCollectionIdentifierConverter.convert(collection.identifier)
|
||||
return NFTCollection(
|
||||
id = collectionId,
|
||||
network = network,
|
||||
name = collection.name,
|
||||
// We use localised strings from resources here to proceed sorting and searching correctly
|
||||
// Sorting is invoked on data layer, searching is simplified to filtering for now and invoked in Model
|
||||
name = when (collectionId) {
|
||||
is NFTCollection.Identifier.EVM -> collection.name
|
||||
is NFTCollection.Identifier.TON -> when {
|
||||
collectionId.contractAddress == null -> resources.getString(R.string.nft_no_collection)
|
||||
collection.name.isNullOrEmpty() -> resources.getString(R.string.nft_untitled_collection)
|
||||
else -> collection.name
|
||||
}
|
||||
is NFTCollection.Identifier.Solana -> when {
|
||||
collectionId.collectionAddress == null -> resources.getString(R.string.nft_no_collection)
|
||||
collection.name.isNullOrEmpty() -> resources.getString(R.string.nft_untitled_collection)
|
||||
else -> collection.name
|
||||
}
|
||||
NFTCollection.Identifier.Unknown -> null
|
||||
},
|
||||
description = collection.description,
|
||||
logoUrl = collection.logoUrl,
|
||||
count = collection.count,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ object NFTSdkCollectionIdentifierConverter : TwoWayConverter<SdkNFTCollection.Id
|
|||
contractAddress = value.contractAddress,
|
||||
)
|
||||
is SdkNFTCollection.Identifier.Solana -> NFTCollection.Identifier.Solana(
|
||||
collection = value.collection,
|
||||
collectionAddress = value.collectionAddress,
|
||||
)
|
||||
is SdkNFTCollection.Identifier.Unknown -> NFTCollection.Identifier.Unknown
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ object NFTSdkCollectionIdentifierConverter : TwoWayConverter<SdkNFTCollection.Id
|
|||
contractAddress = value.contractAddress,
|
||||
)
|
||||
is NFTCollection.Identifier.Solana -> SdkNFTCollection.Identifier.Solana(
|
||||
collection = value.collection,
|
||||
collectionAddress = value.collectionAddress,
|
||||
)
|
||||
is NFTCollection.Identifier.Unknown -> SdkNFTCollection.Identifier.Unknown
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,6 @@ dependencies {
|
|||
implementation(tangemDeps.card.core)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.core)
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.data.nft
|
||||
|
||||
import android.content.res.Resources
|
||||
import arrow.core.Either
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
|
|
@ -48,6 +49,7 @@ internal class DefaultNFTRepository @Inject constructor(
|
|||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val nftFeatureToggles: NFTFeatureToggles,
|
||||
resources: Resources,
|
||||
) : NFTRepository {
|
||||
|
||||
private val networkJobs = ConcurrentHashMap<Network, JobHolder>()
|
||||
|
|
@ -59,6 +61,7 @@ internal class DefaultNFTRepository @Inject constructor(
|
|||
|
||||
private val collectionIdConverter = NFTSdkCollectionIdentifierConverter
|
||||
private val assetIdConverter = NFTSdkAssetIdentifierConverter
|
||||
private val nftSdkCollectionConverter by lazy { NFTSdkCollectionConverter(resources) }
|
||||
|
||||
override fun observeCollections(userWalletId: UserWalletId, networks: List<Network>): Flow<List<NFTCollections>> =
|
||||
flow { emitAll(observeCollectionsInternal(userWalletId, networks)) }
|
||||
|
|
@ -319,7 +322,7 @@ internal class DefaultNFTRepository @Inject constructor(
|
|||
content = NFTCollections.Content.Collections(
|
||||
collections = collections
|
||||
.map { collection ->
|
||||
NFTSdkCollectionConverter.convert(network to collection)
|
||||
nftSdkCollectionConverter.convert(network to collection)
|
||||
}
|
||||
.filter {
|
||||
it.id !is NFTCollection.Identifier.Unknown
|
||||
|
|
@ -417,7 +420,7 @@ internal class DefaultNFTRepository @Inject constructor(
|
|||
content = NFTCollections.Content.Collections(
|
||||
collections = it
|
||||
?.map { collection ->
|
||||
NFTSdkCollectionConverter.convert(network to collection)
|
||||
nftSdkCollectionConverter.convert(network to collection)
|
||||
}
|
||||
?.filter {
|
||||
it.id !is NFTCollection.Identifier.Unknown
|
||||
|
|
|
|||
|
|
@ -1,18 +1,45 @@
|
|||
package com.tangem.data.nft.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.nft.DefaultNFTRepository
|
||||
import com.tangem.datasource.local.nft.NFTPersistenceStoreFactory
|
||||
import com.tangem.datasource.local.nft.NFTRuntimeStoreFactory
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.nft.repository.NFTRepository
|
||||
import dagger.Binds
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.features.nft.NFTFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface NFTDataModule {
|
||||
internal object NFTDataModule {
|
||||
|
||||
@Binds
|
||||
@Provides
|
||||
@Singleton
|
||||
fun bindNFTRepository(repository: DefaultNFTRepository): NFTRepository
|
||||
fun provideNFTRepository(
|
||||
@ApplicationContext context: Context,
|
||||
nftPersistenceStoreFactory: NFTPersistenceStoreFactory,
|
||||
nftRuntimeStoreFactory: NFTRuntimeStoreFactory,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
nftFeatureToggles: NFTFeatureToggles,
|
||||
): NFTRepository = DefaultNFTRepository(
|
||||
nftPersistenceStoreFactory = nftPersistenceStoreFactory,
|
||||
nftRuntimeStoreFactory = nftRuntimeStoreFactory,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
dispatchers = dispatchers,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
userWalletsStore = userWalletsStore,
|
||||
nftFeatureToggles = nftFeatureToggles,
|
||||
resources = context.resources,
|
||||
)
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ data class NFTCollection(
|
|||
data class TON(val contractAddress: String?) : Identifier()
|
||||
|
||||
@Serializable
|
||||
data class Solana(val collection: String?) : Identifier()
|
||||
data class Solana(val collectionAddress: String?) : Identifier()
|
||||
|
||||
@Serializable
|
||||
data object Unknown : Identifier()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
|
||||
internal data class NFTCollectionUM(
|
||||
val id: String,
|
||||
val name: String?,
|
||||
val name: String,
|
||||
@DrawableRes val networkIconId: Int,
|
||||
val logoUrl: String?,
|
||||
val description: TextReference,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import kotlinx.collections.immutable.toPersistentList
|
|||
@Suppress("LongParameterList")
|
||||
internal class UpdateDataStateTransformer(
|
||||
private val nftCollections: List<NFTCollections>,
|
||||
private val searchQuery: String,
|
||||
private val onReceiveClick: () -> Unit,
|
||||
private val onRetryClick: () -> Unit,
|
||||
private val onExpandCollectionClick: (NFTCollection) -> Unit,
|
||||
|
|
@ -55,7 +54,7 @@ internal class UpdateDataStateTransformer(
|
|||
.map { it.content }
|
||||
.asSequence()
|
||||
.filterIsInstance<NFTCollections.Content.Collections>()
|
||||
.map { it.collections.orEmpty().transform(prevState, searchQuery) }
|
||||
.map { it.collections.orEmpty().transform(prevState) }
|
||||
.flatten()
|
||||
.toPersistentList(),
|
||||
warnings = transformNotifications(),
|
||||
|
|
@ -74,48 +73,23 @@ internal class UpdateDataStateTransformer(
|
|||
)
|
||||
}
|
||||
|
||||
private fun List<NFTCollection>.transform(
|
||||
state: NFTCollectionsStateUM,
|
||||
query: String,
|
||||
): ImmutableList<NFTCollectionUM> = mapNotNull {
|
||||
val assetsFulfillQuery = if (query.isEmpty()) {
|
||||
true
|
||||
} else {
|
||||
when (val assets = it.assets) {
|
||||
is NFTCollection.Assets.Empty,
|
||||
is NFTCollection.Assets.Failed,
|
||||
is NFTCollection.Assets.Loading,
|
||||
-> false
|
||||
is NFTCollection.Assets.Value -> {
|
||||
assets.items.any { asset ->
|
||||
asset.name?.lowercase()?.contains(query.lowercase()) == true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val collectionFulfillQuery = query.isEmpty() || it.name?.lowercase()?.contains(query.lowercase()) == true
|
||||
|
||||
if (collectionFulfillQuery || assetsFulfillQuery) {
|
||||
NFTCollectionUM(
|
||||
id = it.collectionIdProvider(),
|
||||
networkIconId = getActiveIconRes(it.network.id.value),
|
||||
name = it.name,
|
||||
description = TextReference.PluralRes(
|
||||
R.plurals.nft_collections_count,
|
||||
it.count,
|
||||
wrappedList(it.count),
|
||||
),
|
||||
logoUrl = it.logoUrl,
|
||||
assets = it.transformAssets(),
|
||||
onExpandClick = {
|
||||
onExpandCollectionClick(it)
|
||||
},
|
||||
isExpanded = it.isExpanded(state),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
private fun List<NFTCollection>.transform(state: NFTCollectionsStateUM): ImmutableList<NFTCollectionUM> = map {
|
||||
NFTCollectionUM(
|
||||
id = it.collectionIdProvider(),
|
||||
networkIconId = getActiveIconRes(it.network.id.value),
|
||||
name = it.name.orEmpty(),
|
||||
description = TextReference.PluralRes(
|
||||
R.plurals.nft_collections_count,
|
||||
it.count,
|
||||
wrappedList(it.count),
|
||||
),
|
||||
logoUrl = it.logoUrl,
|
||||
assets = it.transformAssets(),
|
||||
onExpandClick = {
|
||||
onExpandCollectionClick(it)
|
||||
},
|
||||
isExpanded = it.isExpanded(state),
|
||||
)
|
||||
}.toPersistentList()
|
||||
|
||||
private fun transformNotifications(): ImmutableList<NFTCollectionsWarningUM> = buildList {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.domain.nft.FetchNFTCollectionAssetsUseCase
|
|||
import com.tangem.domain.nft.GetNFTCollectionsUseCase
|
||||
import com.tangem.domain.nft.RefreshAllNFTUseCase
|
||||
import com.tangem.domain.nft.models.NFTCollection
|
||||
import com.tangem.domain.nft.models.NFTCollections
|
||||
import com.tangem.features.nft.collections.NFTCollectionsComponent
|
||||
import com.tangem.features.nft.collections.entity.NFTCollectionsStateUM
|
||||
import com.tangem.features.nft.collections.entity.NFTCollectionsUM
|
||||
|
|
@ -71,8 +72,7 @@ internal class NFTCollectionsModel @Inject constructor(
|
|||
) { nftCollections, query ->
|
||||
_state.update {
|
||||
UpdateDataStateTransformer(
|
||||
nftCollections = nftCollections,
|
||||
searchQuery = query,
|
||||
nftCollections = nftCollections.filter(query),
|
||||
onReceiveClick = {
|
||||
params.onReceiveClick()
|
||||
},
|
||||
|
|
@ -91,6 +91,38 @@ internal class NFTCollectionsModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun List<NFTCollections>.filter(query: String): List<NFTCollections> = map {
|
||||
it.copy(
|
||||
content = when (val content = it.content) {
|
||||
is NFTCollections.Content.Collections -> content.copy(
|
||||
collections = content.collections.orEmpty().filter {
|
||||
val assetsFulfillQuery = if (query.isEmpty()) {
|
||||
true
|
||||
} else {
|
||||
when (val assets = it.assets) {
|
||||
is NFTCollection.Assets.Empty,
|
||||
is NFTCollection.Assets.Failed,
|
||||
is NFTCollection.Assets.Loading,
|
||||
-> false
|
||||
is NFTCollection.Assets.Value -> {
|
||||
assets.items.any { asset ->
|
||||
asset.name?.lowercase()?.contains(query.lowercase()) == true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val collectionFulfillQuery =
|
||||
query.isEmpty() || it.name?.lowercase()?.contains(query.lowercase()) == true
|
||||
|
||||
collectionFulfillQuery || assetsFulfillQuery
|
||||
},
|
||||
)
|
||||
is NFTCollections.Content.Error -> it.content
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun onRefresh() {
|
||||
modelScope.launch {
|
||||
_state.update { ChangeRefreshingStateTransformer(true).transform(it) }
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.nft.collections.entity.NFTCollectionAssetsListUM
|
||||
|
|
@ -90,7 +89,7 @@ private fun RowScope.Text(state: NFTCollectionUM) {
|
|||
) {
|
||||
Text(
|
||||
modifier = Modifier,
|
||||
text = state.name.takeUnless { it.isNullOrEmpty() } ?: stringResourceSafe(R.string.nft_no_collection),
|
||||
text = state.name,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# https://github.com/tangem/tangem-sdk-android/
|
||||
# https://github.com/tangem/vico
|
||||
|
||||
tangemBlockchainSdk = "develop-1063"
|
||||
tangemBlockchainSdk = "develop-1064"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-468"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
@ -21,4 +21,4 @@ card-core = { module = "com.tangem.tangem-sdk-kotlin:core", version.ref = "tange
|
|||
|
||||
vico-compose = { group = "com.tangem.vico", name = "compose", version.ref = "tangemVico" }
|
||||
vico-compose-m3 = { group = "com.tangem.vico", name = "compose-m3", version.ref = "tangemVico" }
|
||||
vico-core = { group = "com.tangem.vico", name = "core", version.ref = "tangemVico" }
|
||||
vico-core = { group = "com.tangem.vico", name = "core", version.ref = "tangemVico" }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue