Updated on 2026-08-14
This commit is contained in:
parent
c7151d95a7
commit
c227fd1001
18 changed files with 299 additions and 187 deletions
|
|
@ -57,7 +57,7 @@ internal object NFTDomainModule {
|
||||||
fun providesGetNFTAvailableNetworksUseCase(
|
fun providesGetNFTAvailableNetworksUseCase(
|
||||||
nftRepository: NFTRepository,
|
nftRepository: NFTRepository,
|
||||||
currenciesRepository: CurrenciesRepository,
|
currenciesRepository: CurrenciesRepository,
|
||||||
): GetNFTAvailableNetworksUseCase = GetNFTAvailableNetworksUseCase(
|
): GetNFTNetworksUseCase = GetNFTNetworksUseCase(
|
||||||
currenciesRepository = currenciesRepository,
|
currenciesRepository = currenciesRepository,
|
||||||
nftRepository = nftRepository,
|
nftRepository = nftRepository,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -546,7 +546,6 @@
|
||||||
<string name="nft_receive_unavailable_asset_warning_message">You haven\'t added this network yet. To start receiving NFTs, add it to the main screen!</string>
|
<string name="nft_receive_unavailable_asset_warning_message">You haven\'t added this network yet. To start receiving NFTs, add it to the main screen!</string>
|
||||||
<string name="nft_receive_unavailable_asset_warning_title">Network not added</string>
|
<string name="nft_receive_unavailable_asset_warning_title">Network not added</string>
|
||||||
<string name="nft_receive_unavailable_section_title">Not Added</string>
|
<string name="nft_receive_unavailable_section_title">Not Added</string>
|
||||||
<string name="nft_send">Send NFT</string>
|
|
||||||
<string name="nft_traits_title">Traits</string>
|
<string name="nft_traits_title">Traits</string>
|
||||||
<string name="nft_wallet_count">%1$d NFTs in %2$d collection</string>
|
<string name="nft_wallet_count">%1$d NFTs in %2$d collection</string>
|
||||||
<string name="nft_wallet_receive_nft">Tap here to receive first NFT</string>
|
<string name="nft_wallet_receive_nft">Tap here to receive first NFT</string>
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ fun ChainRow(model: ChainRowUM, modifier: Modifier = Modifier, action: @Composab
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
|
isEnabled = model.enabled,
|
||||||
accentMainText = true,
|
accentMainText = true,
|
||||||
accentSecondText = false,
|
accentSecondText = false,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,5 @@ data class ChainRowUM(
|
||||||
val type: String,
|
val type: String,
|
||||||
val icon: CurrencyIconState,
|
val icon: CurrencyIconState,
|
||||||
val showCustom: Boolean,
|
val showCustom: Boolean,
|
||||||
|
val enabled: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|
@ -2,7 +2,9 @@ package com.tangem.data.nft
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.blockchain.common.Blockchain
|
import com.tangem.blockchain.common.Blockchain
|
||||||
|
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||||
|
import com.tangem.data.common.currency.getNetwork
|
||||||
import com.tangem.datasource.local.nft.NFTPersistenceStore
|
import com.tangem.datasource.local.nft.NFTPersistenceStore
|
||||||
import com.tangem.datasource.local.nft.NFTPersistenceStoreFactory
|
import com.tangem.datasource.local.nft.NFTPersistenceStoreFactory
|
||||||
import com.tangem.datasource.local.nft.NFTRuntimeStore
|
import com.tangem.datasource.local.nft.NFTRuntimeStore
|
||||||
|
|
@ -10,6 +12,7 @@ import com.tangem.datasource.local.nft.NFTRuntimeStoreFactory
|
||||||
import com.tangem.datasource.local.nft.converter.NFTSdkAssetIdentifierConverter
|
import com.tangem.datasource.local.nft.converter.NFTSdkAssetIdentifierConverter
|
||||||
import com.tangem.datasource.local.nft.converter.NFTSdkCollectionConverter
|
import com.tangem.datasource.local.nft.converter.NFTSdkCollectionConverter
|
||||||
import com.tangem.datasource.local.nft.converter.NFTSdkCollectionIdentifierConverter
|
import com.tangem.datasource.local.nft.converter.NFTSdkCollectionIdentifierConverter
|
||||||
|
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||||
import com.tangem.domain.models.StatusSource
|
import com.tangem.domain.models.StatusSource
|
||||||
import com.tangem.domain.nft.models.NFTAsset
|
import com.tangem.domain.nft.models.NFTAsset
|
||||||
import com.tangem.domain.nft.models.NFTCollection
|
import com.tangem.domain.nft.models.NFTCollection
|
||||||
|
|
@ -37,6 +40,8 @@ internal class DefaultNFTRepository @Inject constructor(
|
||||||
private val nftRuntimeStoreFactory: NFTRuntimeStoreFactory,
|
private val nftRuntimeStoreFactory: NFTRuntimeStoreFactory,
|
||||||
private val walletManagersFacade: WalletManagersFacade,
|
private val walletManagersFacade: WalletManagersFacade,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
private val excludedBlockchains: ExcludedBlockchains,
|
||||||
|
private val userWalletsStore: UserWalletsStore,
|
||||||
) : NFTRepository {
|
) : NFTRepository {
|
||||||
|
|
||||||
private val networkJobs = ConcurrentHashMap<Network, JobHolder>()
|
private val networkJobs = ConcurrentHashMap<Network, JobHolder>()
|
||||||
|
|
@ -129,6 +134,24 @@ internal class DefaultNFTRepository @Inject constructor(
|
||||||
|
|
||||||
override suspend fun isNFTSupported(network: Network): Boolean = network.canHandleNFTs()
|
override suspend fun isNFTSupported(network: Network): Boolean = network.canHandleNFTs()
|
||||||
|
|
||||||
|
override suspend fun getNFTSupportedNetworks(userWalletId: UserWalletId): List<Network> {
|
||||||
|
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||||
|
"User wallet [$userWalletId] not found"
|
||||||
|
}
|
||||||
|
return Blockchain
|
||||||
|
.entries
|
||||||
|
.filter { it.canHandleNFTs() }
|
||||||
|
.filter { !it.isTestnet() }
|
||||||
|
.mapNotNull {
|
||||||
|
getNetwork(
|
||||||
|
blockchain = it,
|
||||||
|
extraDerivationPath = null,
|
||||||
|
scanResponse = userWallet.scanResponse,
|
||||||
|
excludedBlockchains = excludedBlockchains,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String? =
|
override suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String? =
|
||||||
walletManagersFacade.getNFTExploreUrl(
|
walletManagersFacade.getNFTExploreUrl(
|
||||||
network = network,
|
network = network,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.tangem.domain.nft.models
|
||||||
|
|
||||||
|
import com.tangem.domain.tokens.model.Network
|
||||||
|
|
||||||
|
data class NFTNetworks(
|
||||||
|
val availableNetworks: List<Network>,
|
||||||
|
val notAvailableNetworks: List<Network>,
|
||||||
|
)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.domain.nft
|
package com.tangem.domain.nft
|
||||||
|
|
||||||
|
import com.tangem.domain.nft.models.NFTNetworks
|
||||||
import com.tangem.domain.tokens.model.Network
|
import com.tangem.domain.tokens.model.Network
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
@ -7,11 +8,16 @@ import kotlinx.coroutines.withContext
|
||||||
class FilterNFTAvailableNetworksUseCase(
|
class FilterNFTAvailableNetworksUseCase(
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) {
|
) {
|
||||||
suspend operator fun invoke(networks: List<Network>, searchQuery: String): List<Network> =
|
suspend operator fun invoke(networks: NFTNetworks, searchQuery: String): NFTNetworks =
|
||||||
withContext(dispatchers.default) {
|
withContext(dispatchers.default) {
|
||||||
networks.filter {
|
NFTNetworks(
|
||||||
it.name.contains(searchQuery, ignoreCase = true) ||
|
availableNetworks = networks.availableNetworks.filter(searchQuery),
|
||||||
it.currencySymbol.contains(searchQuery, ignoreCase = true)
|
notAvailableNetworks = networks.notAvailableNetworks.filter(searchQuery),
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun List<Network>.filter(searchQuery: String) = filter {
|
||||||
|
it.name.contains(searchQuery, ignoreCase = true) ||
|
||||||
|
it.currencySymbol.contains(searchQuery, ignoreCase = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
package com.tangem.domain.nft
|
|
||||||
|
|
||||||
import com.tangem.domain.nft.repository.NFTRepository
|
|
||||||
import com.tangem.domain.tokens.model.Network
|
|
||||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
|
|
||||||
class GetNFTAvailableNetworksUseCase(
|
|
||||||
private val currenciesRepository: CurrenciesRepository,
|
|
||||||
private val nftRepository: NFTRepository,
|
|
||||||
) {
|
|
||||||
operator fun invoke(userWalletId: UserWalletId): Flow<List<Network>> = currenciesRepository
|
|
||||||
.getWalletCurrenciesUpdates(userWalletId)
|
|
||||||
.map { cryptoCurrencies ->
|
|
||||||
cryptoCurrencies
|
|
||||||
.map { cryptoCurrency -> cryptoCurrency.network }
|
|
||||||
.distinct()
|
|
||||||
.filter { nftRepository.isNFTSupported(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.tangem.domain.nft
|
||||||
|
|
||||||
|
import com.tangem.domain.nft.models.NFTNetworks
|
||||||
|
import com.tangem.domain.nft.repository.NFTRepository
|
||||||
|
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
class GetNFTNetworksUseCase(
|
||||||
|
private val currenciesRepository: CurrenciesRepository,
|
||||||
|
private val nftRepository: NFTRepository,
|
||||||
|
) {
|
||||||
|
operator fun invoke(userWalletId: UserWalletId): Flow<NFTNetworks> = currenciesRepository
|
||||||
|
.getWalletCurrenciesUpdates(userWalletId)
|
||||||
|
.map { cryptoCurrencies ->
|
||||||
|
val availableNetworks = cryptoCurrencies
|
||||||
|
.map { cryptoCurrency -> cryptoCurrency.network }
|
||||||
|
.filter { nftRepository.isNFTSupported(it) }
|
||||||
|
.sortedBy { it.name }
|
||||||
|
|
||||||
|
val notAvailableNetworks = nftRepository
|
||||||
|
.getNFTSupportedNetworks(userWalletId)
|
||||||
|
.filter { supportedNetwork -> availableNetworks.none { it.id == supportedNetwork.id } }
|
||||||
|
.sortedBy { it.name }
|
||||||
|
|
||||||
|
NFTNetworks(
|
||||||
|
availableNetworks = availableNetworks,
|
||||||
|
notAvailableNetworks = notAvailableNetworks,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,5 +18,7 @@ interface NFTRepository {
|
||||||
|
|
||||||
suspend fun isNFTSupported(network: Network): Boolean
|
suspend fun isNFTSupported(network: Network): Boolean
|
||||||
|
|
||||||
|
suspend fun getNFTSupportedNetworks(userWalletId: UserWalletId): List<Network>
|
||||||
|
|
||||||
suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String?
|
suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String?
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
package com.tangem.features.nft.receive.entity
|
package com.tangem.features.nft.receive.entity
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes
|
import com.tangem.core.ui.components.rows.model.ChainRowUM
|
||||||
|
|
||||||
internal data class NFTNetworkUM(
|
internal data class NFTNetworkUM(
|
||||||
val id: String,
|
val id: String,
|
||||||
@DrawableRes val iconRes: Int,
|
val chainRowUM: ChainRowUM,
|
||||||
val name: String,
|
|
||||||
val onItemClick: () -> Unit,
|
val onItemClick: () -> Unit,
|
||||||
)
|
)
|
||||||
|
|
@ -19,7 +19,8 @@ internal data class NFTReceiveUM(
|
||||||
data object Empty : Networks()
|
data object Empty : Networks()
|
||||||
|
|
||||||
data class Content(
|
data class Content(
|
||||||
val items: ImmutableList<NFTNetworkUM>,
|
val availableItems: ImmutableList<NFTNetworkUM>,
|
||||||
|
val notAvailableItems: ImmutableList<NFTNetworkUM>,
|
||||||
) : Networks()
|
) : Networks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package com.tangem.features.nft.receive.entity.transformer
|
package com.tangem.features.nft.receive.entity.transformer
|
||||||
|
|
||||||
|
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||||
|
import com.tangem.core.ui.components.rows.model.ChainRowUM
|
||||||
import com.tangem.core.ui.extensions.getActiveIconRes
|
import com.tangem.core.ui.extensions.getActiveIconRes
|
||||||
|
import com.tangem.domain.nft.models.NFTNetworks
|
||||||
import com.tangem.domain.tokens.model.Network
|
import com.tangem.domain.tokens.model.Network
|
||||||
import com.tangem.features.nft.receive.entity.NFTNetworkUM
|
import com.tangem.features.nft.receive.entity.NFTNetworkUM
|
||||||
import com.tangem.features.nft.receive.entity.NFTReceiveUM
|
import com.tangem.features.nft.receive.entity.NFTReceiveUM
|
||||||
|
|
@ -8,27 +11,44 @@ import com.tangem.utils.transformer.Transformer
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
||||||
internal class UpdateDataStateTransformer(
|
internal class UpdateDataStateTransformer(
|
||||||
private val networks: List<Network>,
|
private val networks: NFTNetworks,
|
||||||
private val onNetworkClick: (Network) -> Unit,
|
private val onNetworkClick: (Network, Boolean) -> Unit,
|
||||||
) : Transformer<NFTReceiveUM> {
|
) : Transformer<NFTReceiveUM> {
|
||||||
|
|
||||||
override fun transform(prevState: NFTReceiveUM): NFTReceiveUM = prevState.copy(
|
override fun transform(prevState: NFTReceiveUM): NFTReceiveUM = prevState.copy(
|
||||||
networks = when {
|
networks = when {
|
||||||
networks.isEmpty() -> NFTReceiveUM.Networks.Empty
|
networks.availableNetworks.isEmpty() && networks.notAvailableNetworks.isEmpty()
|
||||||
|
-> NFTReceiveUM.Networks.Empty
|
||||||
else -> NFTReceiveUM.Networks.Content(
|
else -> NFTReceiveUM.Networks.Content(
|
||||||
items = networks
|
availableItems = networks.availableNetworks
|
||||||
.map { it.transform() }
|
.map { it.transform(true) }
|
||||||
|
.toPersistentList(),
|
||||||
|
notAvailableItems = networks.notAvailableNetworks
|
||||||
|
.map { it.transform(false) }
|
||||||
.toPersistentList(),
|
.toPersistentList(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun Network.transform(): NFTNetworkUM = NFTNetworkUM(
|
private fun Network.transform(enabled: Boolean): NFTNetworkUM {
|
||||||
id = id.value,
|
val custom = derivationPath is Network.DerivationPath.Custom
|
||||||
iconRes = getActiveIconRes(id.value),
|
return NFTNetworkUM(
|
||||||
name = name,
|
id = id.value + derivationPath.value,
|
||||||
onItemClick = {
|
chainRowUM = ChainRowUM(
|
||||||
onNetworkClick(this)
|
name = name,
|
||||||
},
|
type = "",
|
||||||
)
|
icon = CurrencyIconState.CoinIcon(
|
||||||
|
url = null,
|
||||||
|
fallbackResId = getActiveIconRes(id.value),
|
||||||
|
isGrayscale = !enabled,
|
||||||
|
showCustomBadge = custom,
|
||||||
|
),
|
||||||
|
showCustom = custom,
|
||||||
|
enabled = enabled,
|
||||||
|
),
|
||||||
|
onItemClick = {
|
||||||
|
onNetworkClick(this, enabled)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,14 +4,16 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||||
import com.tangem.core.decompose.di.ModelScoped
|
import com.tangem.core.decompose.di.ModelScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
import com.tangem.core.decompose.model.ParamsContainer
|
import com.tangem.core.decompose.model.ParamsContainer
|
||||||
|
import com.tangem.core.decompose.ui.UiMessageSender
|
||||||
import com.tangem.core.navigation.share.ShareManager
|
import com.tangem.core.navigation.share.ShareManager
|
||||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||||
import com.tangem.core.ui.components.fields.InputManager
|
import com.tangem.core.ui.components.fields.InputManager
|
||||||
import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.extensions.wrappedList
|
import com.tangem.core.ui.extensions.wrappedList
|
||||||
|
import com.tangem.core.ui.message.DialogMessage
|
||||||
import com.tangem.domain.nft.FilterNFTAvailableNetworksUseCase
|
import com.tangem.domain.nft.FilterNFTAvailableNetworksUseCase
|
||||||
import com.tangem.domain.nft.GetNFTAvailableNetworksUseCase
|
import com.tangem.domain.nft.GetNFTNetworksUseCase
|
||||||
import com.tangem.domain.nft.GetNFTNetworkStatusUseCase
|
import com.tangem.domain.nft.GetNFTNetworkStatusUseCase
|
||||||
import com.tangem.domain.nft.analytics.NFTAnalyticsEvent
|
import com.tangem.domain.nft.analytics.NFTAnalyticsEvent
|
||||||
import com.tangem.domain.tokens.model.Network
|
import com.tangem.domain.tokens.model.Network
|
||||||
|
|
@ -34,12 +36,13 @@ import javax.inject.Inject
|
||||||
internal class NFTReceiveModel @Inject constructor(
|
internal class NFTReceiveModel @Inject constructor(
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val searchManager: InputManager,
|
private val searchManager: InputManager,
|
||||||
private val getNFTAvailableNetworksUseCase: GetNFTAvailableNetworksUseCase,
|
private val getNFTNetworksUseCase: GetNFTNetworksUseCase,
|
||||||
private val filterNFTAvailableNetworksUseCase: FilterNFTAvailableNetworksUseCase,
|
private val filterNFTAvailableNetworksUseCase: FilterNFTAvailableNetworksUseCase,
|
||||||
private val getNFTNetworkStatusUseCase: GetNFTNetworkStatusUseCase,
|
private val getNFTNetworkStatusUseCase: GetNFTNetworkStatusUseCase,
|
||||||
private val clipboardManager: ClipboardManager,
|
private val clipboardManager: ClipboardManager,
|
||||||
private val shareManager: ShareManager,
|
private val shareManager: ShareManager,
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
|
private val messageSender: UiMessageSender,
|
||||||
paramsContainer: ParamsContainer,
|
paramsContainer: ParamsContainer,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
|
|
@ -53,7 +56,10 @@ internal class NFTReceiveModel @Inject constructor(
|
||||||
formatArgs = wrappedList(params.walletName),
|
formatArgs = wrappedList(params.walletName),
|
||||||
),
|
),
|
||||||
search = getInitialSearchBar(),
|
search = getInitialSearchBar(),
|
||||||
networks = NFTReceiveUM.Networks.Content(persistentListOf()),
|
networks = NFTReceiveUM.Networks.Content(
|
||||||
|
availableItems = persistentListOf(),
|
||||||
|
notAvailableItems = persistentListOf(),
|
||||||
|
),
|
||||||
bottomSheetConfig = null,
|
bottomSheetConfig = null,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -67,7 +73,7 @@ internal class NFTReceiveModel @Inject constructor(
|
||||||
|
|
||||||
private fun subscribeToNFTAvailableNetworks() {
|
private fun subscribeToNFTAvailableNetworks() {
|
||||||
combine(
|
combine(
|
||||||
flow = getNFTAvailableNetworksUseCase(params.userWalletId),
|
flow = getNFTNetworksUseCase(params.userWalletId),
|
||||||
flow2 = searchManager.query.distinctUntilChanged(),
|
flow2 = searchManager.query.distinctUntilChanged(),
|
||||||
) { networks, query ->
|
) { networks, query ->
|
||||||
filterNFTAvailableNetworksUseCase(networks, query)
|
filterNFTAvailableNetworksUseCase(networks, query)
|
||||||
|
|
@ -113,31 +119,40 @@ internal class NFTReceiveModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onNetworkClick(network: Network) {
|
private fun onNetworkClick(network: Network, enabled: Boolean) {
|
||||||
modelScope.launch {
|
if (!enabled) {
|
||||||
analyticsEventHandler.send(NFTAnalyticsEvent.Receive.BlockchainChosen(network.name))
|
val message = DialogMessage(
|
||||||
|
title = resourceReference(R.string.nft_receive_unavailable_asset_warning_title),
|
||||||
|
message = resourceReference(R.string.nft_receive_unavailable_asset_warning_message),
|
||||||
|
)
|
||||||
|
|
||||||
val networkStatus = getNFTNetworkStatusUseCase.invoke(
|
messageSender.send(message)
|
||||||
userWalletId = params.userWalletId,
|
} else {
|
||||||
network = network,
|
modelScope.launch {
|
||||||
) ?: return@launch
|
analyticsEventHandler.send(NFTAnalyticsEvent.Receive.BlockchainChosen(network.name))
|
||||||
|
|
||||||
when (val value = networkStatus.value) {
|
val networkStatus = getNFTNetworkStatusUseCase.invoke(
|
||||||
is NetworkStatus.Verified -> {
|
userWalletId = params.userWalletId,
|
||||||
_state.update {
|
network = network,
|
||||||
ShowReceiveBottomSheetTransformer(
|
) ?: return@launch
|
||||||
network = network,
|
|
||||||
networkAddress = value.address,
|
when (val value = networkStatus.value) {
|
||||||
onDismissBottomSheet = ::onReceiveBottomSheetDismiss,
|
is NetworkStatus.Verified -> {
|
||||||
onCopyClick = { text -> onCopyClick(text, network) },
|
_state.update {
|
||||||
onShareClick = { text -> onShareClick(text, network) },
|
ShowReceiveBottomSheetTransformer(
|
||||||
).transform(it)
|
network = network,
|
||||||
|
networkAddress = value.address,
|
||||||
|
onDismissBottomSheet = ::onReceiveBottomSheetDismiss,
|
||||||
|
onCopyClick = { text -> onCopyClick(text, network) },
|
||||||
|
onShareClick = { text -> onShareClick(text, network) },
|
||||||
|
).transform(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
is NetworkStatus.MissedDerivation,
|
||||||
|
is NetworkStatus.NoAccount,
|
||||||
|
is NetworkStatus.Unreachable,
|
||||||
|
-> Unit
|
||||||
}
|
}
|
||||||
is NetworkStatus.MissedDerivation,
|
|
||||||
is NetworkStatus.NoAccount,
|
|
||||||
is NetworkStatus.Unreachable,
|
|
||||||
-> Unit
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
package com.tangem.features.nft.receive.ui
|
|
||||||
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
|
||||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
|
||||||
import com.tangem.features.nft.impl.R
|
|
||||||
import com.tangem.features.nft.receive.entity.NFTNetworkUM
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun NFTNetwork(state: NFTNetworkUM, modifier: Modifier = Modifier) {
|
|
||||||
Row(
|
|
||||||
modifier = modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable { state.onItemClick() }
|
|
||||||
.padding(TangemTheme.dimens.spacing12),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
Image(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(TangemTheme.dimens.size24)
|
|
||||||
.clip(CircleShape),
|
|
||||||
painter = painterResource(state.iconRes),
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
|
||||||
text = state.name,
|
|
||||||
style = TangemTheme.typography.subtitle2,
|
|
||||||
color = TangemTheme.colors.text.primary1,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(widthDp = 360, showBackground = true)
|
|
||||||
@Preview(widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
|
||||||
@Composable
|
|
||||||
private fun Preview_NFTNetwork(@PreviewParameter(NFTNetworkProvider::class) state: NFTNetworkUM) {
|
|
||||||
TangemThemePreview {
|
|
||||||
NFTNetwork(
|
|
||||||
state = state,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NFTNetworkProvider : CollectionPreviewParameterProvider<NFTNetworkUM>(
|
|
||||||
collection = listOf(
|
|
||||||
NFTNetworkUM(
|
|
||||||
id = "item1",
|
|
||||||
name = "Nethers #0853",
|
|
||||||
iconRes = R.drawable.img_eth_22,
|
|
||||||
onItemClick = { },
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package com.tangem.features.nft.receive.ui
|
package com.tangem.features.nft.receive.ui
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.AnimatedContent
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -51,18 +50,9 @@ internal fun NFTReceive(state: NFTReceiveUM, modifier: Modifier = Modifier) {
|
||||||
colors = TangemSearchBarDefaults.secondaryTextFieldColors,
|
colors = TangemSearchBarDefaults.secondaryTextFieldColors,
|
||||||
)
|
)
|
||||||
|
|
||||||
val contentModifier = Modifier
|
when (val networks = state.networks) {
|
||||||
.padding(TangemTheme.dimens.spacing16)
|
is NFTReceiveUM.Networks.Content -> NFTReceiveNetworksContent(networks)
|
||||||
|
is NFTReceiveUM.Networks.Empty -> NFTReceiveNetworksEmpty()
|
||||||
AnimatedContent(
|
|
||||||
targetState = state.networks,
|
|
||||||
contentKey = { it::class },
|
|
||||||
label = "NFT Receive",
|
|
||||||
) {
|
|
||||||
when (val networks = it) {
|
|
||||||
is NFTReceiveUM.Networks.Content -> NFTReceiveNetworksContent(networks, contentModifier)
|
|
||||||
is NFTReceiveUM.Networks.Empty -> NFTReceiveNetworksEmpty(contentModifier)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,29 @@
|
||||||
package com.tangem.features.nft.receive.ui
|
package com.tangem.features.nft.receive.ui
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.composed
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||||
|
import com.tangem.core.ui.components.rows.ChainRow
|
||||||
|
import com.tangem.core.ui.components.rows.model.ChainRowUM
|
||||||
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
import com.tangem.core.ui.extensions.resolveReference
|
||||||
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.features.nft.impl.R
|
import com.tangem.features.nft.impl.R
|
||||||
|
|
@ -27,40 +37,86 @@ internal fun NFTReceiveNetworksContent(state: NFTReceiveUM.Networks.Content, mod
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxSize()
|
||||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
.padding(
|
||||||
.background(TangemTheme.colors.background.primary)
|
vertical = TangemTheme.dimens.spacing8,
|
||||||
.animateContentSize(),
|
),
|
||||||
state = listState,
|
state = listState,
|
||||||
|
contentPadding = PaddingValues(
|
||||||
|
horizontal = TangemTheme.dimens.spacing16,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
item(
|
if (state.availableItems.isNotEmpty()) {
|
||||||
key = "title",
|
item(
|
||||||
) {
|
key = "available_title",
|
||||||
Title()
|
) {
|
||||||
|
Title(resourceReference(R.string.nft_receive_available_section_title))
|
||||||
|
}
|
||||||
|
networks(state.availableItems)
|
||||||
}
|
}
|
||||||
items(
|
if (state.notAvailableItems.isNotEmpty()) {
|
||||||
items = state.items,
|
item(
|
||||||
key = NFTNetworkUM::id,
|
key = "not_available_title",
|
||||||
) { network ->
|
) {
|
||||||
NFTNetwork(
|
Title(resourceReference(R.string.nft_receive_unavailable_section_title))
|
||||||
state = network,
|
}
|
||||||
modifier = Modifier.animateItem(),
|
networks(state.notAvailableItems)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun LazyListScope.networks(items: List<NFTNetworkUM>) {
|
||||||
|
itemsIndexed(
|
||||||
|
items = items,
|
||||||
|
key = { _, item -> item.id },
|
||||||
|
) { index, network ->
|
||||||
|
val isLastItem = index == items.size - 1
|
||||||
|
ChainRow(
|
||||||
|
modifier = Modifier
|
||||||
|
.composed {
|
||||||
|
if (isLastItem) {
|
||||||
|
padding(
|
||||||
|
bottom = TangemTheme.dimens.spacing8,
|
||||||
|
)
|
||||||
|
.clip(
|
||||||
|
RoundedCornerShape(
|
||||||
|
bottomStart = TangemTheme.dimens.radius16,
|
||||||
|
bottomEnd = TangemTheme.dimens.radius16,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background(TangemTheme.colors.background.primary)
|
||||||
|
.clickable { network.onItemClick() },
|
||||||
|
model = network.chainRowUM,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Title(modifier: Modifier = Modifier) {
|
private fun Title(text: TextReference, modifier: Modifier = Modifier) {
|
||||||
Text(
|
Text(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(
|
||||||
|
top = TangemTheme.dimens.spacing8,
|
||||||
|
)
|
||||||
|
.background(
|
||||||
|
color = TangemTheme.colors.background.primary,
|
||||||
|
shape = RoundedCornerShape(
|
||||||
|
topStart = TangemTheme.dimens.radius16,
|
||||||
|
topEnd = TangemTheme.dimens.radius16,
|
||||||
|
),
|
||||||
|
)
|
||||||
.padding(
|
.padding(
|
||||||
start = TangemTheme.dimens.spacing12,
|
start = TangemTheme.dimens.spacing12,
|
||||||
top = TangemTheme.dimens.spacing12,
|
top = TangemTheme.dimens.spacing12,
|
||||||
end = TangemTheme.dimens.spacing12,
|
end = TangemTheme.dimens.spacing12,
|
||||||
bottom = TangemTheme.dimens.spacing4,
|
bottom = TangemTheme.dimens.spacing4,
|
||||||
),
|
),
|
||||||
text = stringResourceSafe(R.string.nft_receive_choose_network),
|
text = text.resolveReference(),
|
||||||
style = TangemTheme.typography.subtitle2,
|
style = TangemTheme.typography.subtitle2,
|
||||||
color = TangemTheme.colors.text.tertiary,
|
color = TangemTheme.colors.text.tertiary,
|
||||||
)
|
)
|
||||||
|
|
@ -74,32 +130,84 @@ private fun Preview_NFTReceiveNetworksUM() {
|
||||||
TangemThemePreview {
|
TangemThemePreview {
|
||||||
NFTReceiveNetworksContent(
|
NFTReceiveNetworksContent(
|
||||||
state = NFTReceiveUM.Networks.Content(
|
state = NFTReceiveUM.Networks.Content(
|
||||||
items = persistentListOf(
|
availableItems = persistentListOf(
|
||||||
NFTNetworkUM(
|
NFTNetworkUM(
|
||||||
id = "item1",
|
id = "item1",
|
||||||
name = "Ethereum",
|
chainRowUM = ChainRowUM(
|
||||||
iconRes = R.drawable.img_eth_22,
|
name = "Ethereum",
|
||||||
|
type = "",
|
||||||
|
icon = CurrencyIconState.CoinIcon(
|
||||||
|
url = null,
|
||||||
|
fallbackResId = R.drawable.img_eth_22,
|
||||||
|
isGrayscale = false,
|
||||||
|
showCustomBadge = false,
|
||||||
|
),
|
||||||
|
showCustom = false,
|
||||||
|
),
|
||||||
onItemClick = { },
|
onItemClick = { },
|
||||||
),
|
),
|
||||||
NFTNetworkUM(
|
NFTNetworkUM(
|
||||||
id = "item2",
|
id = "item2",
|
||||||
name = "Polygon",
|
chainRowUM = ChainRowUM(
|
||||||
iconRes = R.drawable.img_polygon_22,
|
name = "Polygon",
|
||||||
|
type = "",
|
||||||
|
icon = CurrencyIconState.CoinIcon(
|
||||||
|
url = null,
|
||||||
|
fallbackResId = R.drawable.img_polygon_22,
|
||||||
|
isGrayscale = false,
|
||||||
|
showCustomBadge = false,
|
||||||
|
),
|
||||||
|
showCustom = false,
|
||||||
|
),
|
||||||
onItemClick = { },
|
onItemClick = { },
|
||||||
),
|
),
|
||||||
NFTNetworkUM(
|
NFTNetworkUM(
|
||||||
id = "item3",
|
id = "item3",
|
||||||
name = "BSC",
|
chainRowUM = ChainRowUM(
|
||||||
iconRes = R.drawable.img_bsc_22,
|
name = "BSC",
|
||||||
|
type = "",
|
||||||
|
icon = CurrencyIconState.CoinIcon(
|
||||||
|
url = null,
|
||||||
|
fallbackResId = R.drawable.img_bsc_22,
|
||||||
|
isGrayscale = false,
|
||||||
|
showCustomBadge = false,
|
||||||
|
),
|
||||||
|
showCustom = false,
|
||||||
|
),
|
||||||
onItemClick = { },
|
onItemClick = { },
|
||||||
),
|
),
|
||||||
NFTNetworkUM(
|
NFTNetworkUM(
|
||||||
id = "item4",
|
id = "item4",
|
||||||
name = "Avalanche",
|
chainRowUM = ChainRowUM(
|
||||||
iconRes = R.drawable.img_avalanche_22,
|
name = "Avalanche",
|
||||||
|
type = "",
|
||||||
|
icon = CurrencyIconState.CoinIcon(
|
||||||
|
url = null,
|
||||||
|
fallbackResId = R.drawable.img_avalanche_22,
|
||||||
|
isGrayscale = false,
|
||||||
|
showCustomBadge = false,
|
||||||
|
),
|
||||||
|
showCustom = false,
|
||||||
|
),
|
||||||
|
onItemClick = { },
|
||||||
|
),
|
||||||
|
NFTNetworkUM(
|
||||||
|
id = "item5",
|
||||||
|
chainRowUM = ChainRowUM(
|
||||||
|
name = "Avalanche",
|
||||||
|
type = "",
|
||||||
|
icon = CurrencyIconState.CoinIcon(
|
||||||
|
url = null,
|
||||||
|
fallbackResId = R.drawable.img_avalanche_22,
|
||||||
|
isGrayscale = false,
|
||||||
|
showCustomBadge = true,
|
||||||
|
),
|
||||||
|
showCustom = true,
|
||||||
|
),
|
||||||
onItemClick = { },
|
onItemClick = { },
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
notAvailableItems = persistentListOf(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.nft.receive.ui
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
|
@ -16,6 +17,7 @@ internal fun NFTReceiveNetworksEmpty(modifier: Modifier = Modifier) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.padding(TangemTheme.dimens.spacing16)
|
||||||
.background(TangemTheme.colors.background.secondary),
|
.background(TangemTheme.colors.background.secondary),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue