From 17ae97606e4e9f714cd7c6b933414e3c3231e612 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 15 May 2025 23:47:29 +0500 Subject: [PATCH] Updated on 2026-08-14 --- core/datasource/build.gradle.kts | 1 + .../local/nft/DefaultNFTRuntimeStore.kt | 2 +- .../converter/NFTSdkCollectionConverter.kt | 23 ++++++- .../NFTSdkCollectionIdentifierConverter.kt | 4 +- data/nft/build.gradle.kts | 2 +- .../tangem/data/nft/DefaultNFTRepository.kt | 7 ++- .../com/tangem/data/nft/di/NFTDataModule.kt | 35 +++++++++-- .../tangem/domain/nft/models/NFTCollection.kt | 2 +- .../nft/collections/entity/NFTCollectionUM.kt | 2 +- .../transformer/UpdateDataStateTransformer.kt | 62 ++++++------------- .../collections/model/NFTCollectionsModel.kt | 36 ++++++++++- .../nft/collections/ui/NFTCollection.kt | 3 +- gradle/tangem_dependencies.toml | 4 +- 13 files changed, 119 insertions(+), 64 deletions(-) diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index ebde9b43cd..b524e010af 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -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) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/nft/DefaultNFTRuntimeStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/nft/DefaultNFTRuntimeStore.kt index 31a46a4a12..cd70259814 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/nft/DefaultNFTRuntimeStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/nft/DefaultNFTRuntimeStore.kt @@ -122,7 +122,7 @@ internal class DefaultNFTRuntimeStore( ) } ?.filter { it.count > 0 } - ?.sortedBy { it.name }, + ?.sortedBy { it.name?.lowercase() }, source = this.source, ) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkCollectionConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkCollectionConverter.kt index 603e003ccb..93662ba168 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkCollectionConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkCollectionConverter.kt @@ -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, NFTCollection> { +class NFTSdkCollectionConverter( + private val resources: Resources, +) : Converter, NFTCollection> { override fun convert(value: Pair): 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, diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkCollectionIdentifierConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkCollectionIdentifierConverter.kt index 44f1df3f89..f97ef9c812 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkCollectionIdentifierConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkCollectionIdentifierConverter.kt @@ -13,7 +13,7 @@ object NFTSdkCollectionIdentifierConverter : TwoWayConverter NFTCollection.Identifier.Solana( - collection = value.collection, + collectionAddress = value.collectionAddress, ) is SdkNFTCollection.Identifier.Unknown -> NFTCollection.Identifier.Unknown } @@ -26,7 +26,7 @@ object NFTSdkCollectionIdentifierConverter : TwoWayConverter SdkNFTCollection.Identifier.Solana( - collection = value.collection, + collectionAddress = value.collectionAddress, ) is NFTCollection.Identifier.Unknown -> SdkNFTCollection.Identifier.Unknown } diff --git a/data/nft/build.gradle.kts b/data/nft/build.gradle.kts index 2e0f7cb455..63ceb134a2 100644 --- a/data/nft/build.gradle.kts +++ b/data/nft/build.gradle.kts @@ -49,6 +49,6 @@ dependencies { implementation(tangemDeps.card.core) /** DI */ - implementation(deps.hilt.core) + implementation(deps.hilt.android) kapt(deps.hilt.kapt) } \ No newline at end of file diff --git a/data/nft/src/main/kotlin/com/tangem/data/nft/DefaultNFTRepository.kt b/data/nft/src/main/kotlin/com/tangem/data/nft/DefaultNFTRepository.kt index 2747f7c697..d5d3a63d50 100644 --- a/data/nft/src/main/kotlin/com/tangem/data/nft/DefaultNFTRepository.kt +++ b/data/nft/src/main/kotlin/com/tangem/data/nft/DefaultNFTRepository.kt @@ -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() @@ -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): Flow> = 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 diff --git a/data/nft/src/main/kotlin/com/tangem/data/nft/di/NFTDataModule.kt b/data/nft/src/main/kotlin/com/tangem/data/nft/di/NFTDataModule.kt index c0af92def9..e66df70d44 100644 --- a/data/nft/src/main/kotlin/com/tangem/data/nft/di/NFTDataModule.kt +++ b/data/nft/src/main/kotlin/com/tangem/data/nft/di/NFTDataModule.kt @@ -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, + ) } \ No newline at end of file diff --git a/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTCollection.kt b/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTCollection.kt index eee0569d01..c524870cf1 100644 --- a/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTCollection.kt +++ b/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTCollection.kt @@ -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() diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/NFTCollectionUM.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/NFTCollectionUM.kt index 771f9515fc..e7e24ded3b 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/NFTCollectionUM.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/NFTCollectionUM.kt @@ -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, diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt index 9a7e3bc93d..cefa97a073 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt @@ -15,7 +15,6 @@ import kotlinx.collections.immutable.toPersistentList @Suppress("LongParameterList") internal class UpdateDataStateTransformer( private val nftCollections: List, - 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() - .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.transform( - state: NFTCollectionsStateUM, - query: String, - ): ImmutableList = 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.transform(state: NFTCollectionsStateUM): ImmutableList = 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 = buildList { diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt index 1b83ed39e4..bf8bd9382e 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt @@ -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.filter(query: String): List = 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) } diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollection.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollection.kt index 3851542d90..e89b5bbe41 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollection.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollection.kt @@ -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, diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index ccc406b5ff..994dc52f3d 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -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" } \ No newline at end of file +vico-core = { group = "com.tangem.vico", name = "core", version.ref = "tangemVico" }