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 5725c0aecf..753597e44a 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 @@ -9,7 +9,6 @@ import com.tangem.domain.nft.models.NFTSalePrice import com.tangem.domain.tokens.model.Network import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map internal class DefaultNFTRuntimeStore( @@ -25,9 +24,7 @@ internal class DefaultNFTRuntimeStore( override fun getCollections(): Flow = collectionsRuntimeStore .get() - .combine(pricesRuntimeStore.get(), ::Pair) - .map { - val (collectionsData, prices) = it + .combine(pricesRuntimeStore.get()) { collectionsData, prices -> collectionsData.mergeWithPrices(prices) } @@ -48,9 +45,7 @@ internal class DefaultNFTRuntimeStore( override fun getAsset(collectionId: NFTCollection.Identifier, assetId: NFTAsset.Identifier): Flow = collectionsRuntimeStore .get() - .combine(getSalePrice(assetId), ::Pair) - .map { - val (collectionsData, price) = it + .combine(getSalePrice(assetId)) { collectionsData, price -> collectionsData .getCollection(collectionId) ?.getAsset(assetId) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/nft/NFTPersistenceStoreFactory.kt b/core/datasource/src/main/java/com/tangem/datasource/local/nft/NFTPersistenceStoreFactory.kt index 36b72f71d8..49d0ce8415 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/nft/NFTPersistenceStoreFactory.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/nft/NFTPersistenceStoreFactory.kt @@ -26,6 +26,17 @@ class NFTPersistenceStoreFactory @Inject constructor( ) { fun provide(network: Network): NFTPersistenceStore { + val networkStringIdentifier = listOfNotNull( + network.id.value, + network.derivationPath.value, + ).joinToString("_") { + // remove all non-alphanumeric characters + it + .toCharArray() + .filter(Char::isLetterOrDigit) + .joinToString("") + .lowercase() + } return DefaultNFTPersistenceStore( collectionsPersistenceStore = DataStoreFactory.create( serializer = MoshiDataStoreSerializer( @@ -34,7 +45,7 @@ class NFTPersistenceStoreFactory @Inject constructor( defaultValue = emptyList(), ), produceFile = { - context.dataStoreFile(fileName = "nft_${network.name}_${network.derivationPath}_collections") + context.dataStoreFile(fileName = "nft_${networkStringIdentifier}_collections") }, scope = CoroutineScope(context = dispatchers.io + SupervisorJob()), ), @@ -45,7 +56,7 @@ class NFTPersistenceStoreFactory @Inject constructor( defaultValue = emptyMap(), ), produceFile = { - context.dataStoreFile(fileName = "nft_${network.name}_${network.derivationPath}_prices") + context.dataStoreFile(fileName = "nft_${networkStringIdentifier}_prices") }, scope = CoroutineScope(context = dispatchers.io + SupervisorJob()), ), diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkAssetIdentifierConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkAssetIdentifierConverter.kt index db75460ab1..b6a045de7e 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkAssetIdentifierConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/nft/converter/NFTSdkAssetIdentifierConverter.kt @@ -4,7 +4,7 @@ import com.tangem.domain.nft.models.NFTAsset import com.tangem.utils.converter.TwoWayConverter import com.tangem.blockchain.nft.models.NFTAsset as SdkNFTAsset -class NFTSdkAssetIdentifierConverter : TwoWayConverter { +object NFTSdkAssetIdentifierConverter : TwoWayConverter { override fun convert(value: SdkNFTAsset.Identifier): NFTAsset.Identifier = when (value) { is SdkNFTAsset.Identifier.EVM -> NFTAsset.Identifier.EVM( tokenId = value.tokenId, @@ -13,7 +13,7 @@ class NFTSdkAssetIdentifierConverter : TwoWayConverter NFTAsset.Identifier.TON( tokenAddress = value.tokenAddress, ) - is SdkNFTAsset.Identifier.Unknown -> error("unknown asset id") + is SdkNFTAsset.Identifier.Unknown -> NFTAsset.Identifier.Unknown } override fun convertBack(value: NFTAsset.Identifier): SdkNFTAsset.Identifier = when (value) { @@ -24,5 +24,6 @@ class NFTSdkAssetIdentifierConverter : TwoWayConverter SdkNFTAsset.Identifier.TON( tokenAddress = value.tokenAddress, ) + is NFTAsset.Identifier.Unknown -> SdkNFTAsset.Identifier.Unknown } } \ No newline at end of file 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 70fb511bad..7b1be28a4e 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,6 @@ package com.tangem.datasource.local.nft.converter +import com.tangem.domain.nft.models.NFTAsset import com.tangem.domain.nft.models.NFTCollection import com.tangem.domain.tokens.model.Network import com.tangem.utils.converter.Converter @@ -19,9 +20,13 @@ class NFTSdkCollectionConverter( description = collection.description, logoUrl = collection.logoUrl, count = collection.count, - assets = collection.assets.map { asset -> - nftSdkAssetConverter.convert(network to asset) - }, + assets = collection.assets + .map { asset -> + nftSdkAssetConverter.convert(network to asset) + } + .filter { + it.id !is NFTAsset.Identifier.Unknown + }, ) } } \ No newline at end of file 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 9c2a5d385d..98d8e0ee73 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 @@ -4,7 +4,7 @@ import com.tangem.domain.nft.models.NFTCollection import com.tangem.utils.converter.TwoWayConverter import com.tangem.blockchain.nft.models.NFTCollection as SdkNFTCollection -class NFTSdkCollectionIdentifierConverter : TwoWayConverter { +object NFTSdkCollectionIdentifierConverter : TwoWayConverter { override fun convert(value: SdkNFTCollection.Identifier): NFTCollection.Identifier = when (value) { is SdkNFTCollection.Identifier.EVM -> NFTCollection.Identifier.EVM( tokenAddress = value.tokenAddress, @@ -12,7 +12,7 @@ class NFTSdkCollectionIdentifierConverter : TwoWayConverter NFTCollection.Identifier.TON( contractAddress = value.contractAddress, ) - is SdkNFTCollection.Identifier.Unknown -> error("unknown collection id") + is SdkNFTCollection.Identifier.Unknown -> NFTCollection.Identifier.Unknown } override fun convertBack(value: NFTCollection.Identifier): SdkNFTCollection.Identifier = when (value) { @@ -22,5 +22,6 @@ class NFTSdkCollectionIdentifierConverter : TwoWayConverter SdkNFTCollection.Identifier.TON( contractAddress = value.contractAddress, ) + is NFTCollection.Identifier.Unknown -> SdkNFTCollection.Identifier.Unknown } } \ 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 7eb91943cf..f0c4c72fea 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,7 +1,6 @@ package com.tangem.data.nft import arrow.core.Either -import com.tangem.blockchain.nft.models.NFTCollection import com.tangem.datasource.local.nft.NFTPersistenceStore import com.tangem.datasource.local.nft.NFTPersistenceStoreFactory import com.tangem.datasource.local.nft.NFTRuntimeStore @@ -11,6 +10,7 @@ import com.tangem.datasource.local.nft.converter.NFTSdkAssetIdentifierConverter import com.tangem.datasource.local.nft.converter.NFTSdkCollectionConverter import com.tangem.datasource.local.nft.converter.NFTSdkCollectionIdentifierConverter import com.tangem.domain.models.StatusSource +import com.tangem.domain.nft.models.NFTCollection import com.tangem.domain.nft.models.NFTCollections import com.tangem.domain.nft.models.NFTSalePrice import com.tangem.domain.nft.repository.NFTRepository @@ -20,11 +20,8 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.* import kotlinx.coroutines.flow.* -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.launch import javax.inject.Inject import com.tangem.blockchain.nft.models.NFTCollection as SdkNFTCollection @@ -32,24 +29,20 @@ internal class DefaultNFTRepository @Inject constructor( private val nftPersistenceStoreFactory: NFTPersistenceStoreFactory, private val nftRuntimeStoreFactory: NFTRuntimeStoreFactory, private val walletManagersFacade: WalletManagersFacade, - dispatchers: CoroutineDispatcherProvider, + private val dispatchers: CoroutineDispatcherProvider, ) : NFTRepository { - private val scope = CoroutineScope(dispatchers.io + SupervisorJob()) - private val jobs = mutableMapOf() private val nftRuntimeStores = mutableMapOf() private val nftPersistenceStores = mutableMapOf() - private val nftSdkAssetIdentifierConverter = NFTSdkAssetIdentifierConverter() - private val nftSdkCollectionIdentifierConverter = NFTSdkCollectionIdentifierConverter() private val nftSdkAssetConverter = NFTSdkAssetConverter( - nftSdkAssetIdentifierConverter = nftSdkAssetIdentifierConverter, - nftSdkCollectionIdentifierConverter = nftSdkCollectionIdentifierConverter, + nftSdkAssetIdentifierConverter = NFTSdkAssetIdentifierConverter, + nftSdkCollectionIdentifierConverter = NFTSdkCollectionIdentifierConverter, ) private val collectionConverter = NFTSdkCollectionConverter( - nftSdkCollectionIdentifierConverter = nftSdkCollectionIdentifierConverter, + nftSdkCollectionIdentifierConverter = NFTSdkCollectionIdentifierConverter, nftSdkAssetConverter = nftSdkAssetConverter, ) @@ -64,12 +57,11 @@ internal class DefaultNFTRepository @Inject constructor( refreshCollections(userWalletId, networks) } - override suspend fun refreshCollections(userWalletId: UserWalletId, networks: List) { + override suspend fun refreshCollections(userWalletId: UserWalletId, networks: List) = coroutineScope { networks.map { network -> - scope.launch { - expireCollections(network) - + launch(dispatchers.io) { Either.catch { + expireCollections(network) walletManagersFacade.getNFTCollections(userWalletId, network) }.onLeft { saveFailedStateInRuntime( @@ -115,9 +107,13 @@ internal class DefaultNFTRepository @Inject constructor( NFTCollections( network = network, content = NFTCollections.Content.Collections( - collections = collections.map { collection -> - collectionConverter.convert(network to collection) - }, + collections = collections + .map { collection -> + collectionConverter.convert(network to collection) + } + .filter { + it.id !is NFTCollection.Identifier.Unknown + }, source = StatusSource.ACTUAL, ), ), @@ -169,9 +165,13 @@ internal class DefaultNFTRepository @Inject constructor( NFTCollections( network = network, content = NFTCollections.Content.Collections( - collections = it?.map { collection -> - collectionConverter.convert(network to collection) - }, + collections = it + ?.map { collection -> + collectionConverter.convert(network to collection) + } + ?.filter { + it.id !is NFTCollection.Identifier.Unknown + }, source = StatusSource.CACHE, ), ) @@ -184,7 +184,7 @@ internal class DefaultNFTRepository @Inject constructor( prices .mapKeys { val (assetId, _) = it - nftSdkAssetIdentifierConverter.convert(assetId) + NFTSdkAssetIdentifierConverter.convert(assetId) } .mapValues { val (assetId, price) = it @@ -202,7 +202,7 @@ internal class DefaultNFTRepository @Inject constructor( getNFTPersistenceStore(network) .getCollectionsSync() .orEmpty() - .associateBy(NFTCollection::identifier) + .associateBy(SdkNFTCollection::identifier) return this.map { updatedCollection -> if (!storedCollections.containsKey(updatedCollection.identifier)) { diff --git a/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTAsset.kt b/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTAsset.kt index ded2b25e41..f2cbe202c4 100644 --- a/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTAsset.kt +++ b/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTAsset.kt @@ -48,5 +48,7 @@ sealed class NFTAsset { ) : Identifier() data class TON(val tokenAddress: String) : Identifier() + + data object Unknown : Identifier() } } \ 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 27e9ad4737..55563ea813 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 @@ -14,5 +14,6 @@ data class NFTCollection( sealed class Identifier { data class EVM(val tokenAddress: String) : Identifier() data class TON(val contractAddress: String?) : Identifier() + data object Unknown : Identifier() } } \ No newline at end of file