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(
|
||||
nftRepository: NFTRepository,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
): GetNFTAvailableNetworksUseCase = GetNFTAvailableNetworksUseCase(
|
||||
): GetNFTNetworksUseCase = GetNFTNetworksUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
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_title">Network 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_wallet_count">%1$d NFTs in %2$d collection</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 {
|
||||
null
|
||||
},
|
||||
isEnabled = model.enabled,
|
||||
accentMainText = true,
|
||||
accentSecondText = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,4 +9,5 @@ data class ChainRowUM(
|
|||
val type: String,
|
||||
val icon: CurrencyIconState,
|
||||
val showCustom: Boolean,
|
||||
val enabled: Boolean = true,
|
||||
)
|
||||
|
|
@ -2,7 +2,9 @@ package com.tangem.data.nft
|
|||
|
||||
import arrow.core.Either
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
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.NFTPersistenceStoreFactory
|
||||
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.NFTSdkCollectionConverter
|
||||
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.nft.models.NFTAsset
|
||||
import com.tangem.domain.nft.models.NFTCollection
|
||||
|
|
@ -37,6 +40,8 @@ internal class DefaultNFTRepository @Inject constructor(
|
|||
private val nftRuntimeStoreFactory: NFTRuntimeStoreFactory,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
) : NFTRepository {
|
||||
|
||||
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 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? =
|
||||
walletManagersFacade.getNFTExploreUrl(
|
||||
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
|
||||
|
||||
import com.tangem.domain.nft.models.NFTNetworks
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -7,11 +8,16 @@ import kotlinx.coroutines.withContext
|
|||
class FilterNFTAvailableNetworksUseCase(
|
||||
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) {
|
||||
networks.filter {
|
||||
it.name.contains(searchQuery, ignoreCase = true) ||
|
||||
it.currencySymbol.contains(searchQuery, ignoreCase = true)
|
||||
}
|
||||
NFTNetworks(
|
||||
availableNetworks = networks.availableNetworks.filter(searchQuery),
|
||||
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 getNFTSupportedNetworks(userWalletId: UserWalletId): List<Network>
|
||||
|
||||
suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String?
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
package com.tangem.features.nft.receive.entity
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.components.rows.model.ChainRowUM
|
||||
|
||||
internal data class NFTNetworkUM(
|
||||
val id: String,
|
||||
@DrawableRes val iconRes: Int,
|
||||
val name: String,
|
||||
val chainRowUM: ChainRowUM,
|
||||
val onItemClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -19,7 +19,8 @@ internal data class NFTReceiveUM(
|
|||
data object Empty : Networks()
|
||||
|
||||
data class Content(
|
||||
val items: ImmutableList<NFTNetworkUM>,
|
||||
val availableItems: ImmutableList<NFTNetworkUM>,
|
||||
val notAvailableItems: ImmutableList<NFTNetworkUM>,
|
||||
) : Networks()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
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.domain.nft.models.NFTNetworks
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.features.nft.receive.entity.NFTNetworkUM
|
||||
import com.tangem.features.nft.receive.entity.NFTReceiveUM
|
||||
|
|
@ -8,27 +11,44 @@ import com.tangem.utils.transformer.Transformer
|
|||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal class UpdateDataStateTransformer(
|
||||
private val networks: List<Network>,
|
||||
private val onNetworkClick: (Network) -> Unit,
|
||||
private val networks: NFTNetworks,
|
||||
private val onNetworkClick: (Network, Boolean) -> Unit,
|
||||
) : Transformer<NFTReceiveUM> {
|
||||
|
||||
override fun transform(prevState: NFTReceiveUM): NFTReceiveUM = prevState.copy(
|
||||
networks = when {
|
||||
networks.isEmpty() -> NFTReceiveUM.Networks.Empty
|
||||
networks.availableNetworks.isEmpty() && networks.notAvailableNetworks.isEmpty()
|
||||
-> NFTReceiveUM.Networks.Empty
|
||||
else -> NFTReceiveUM.Networks.Content(
|
||||
items = networks
|
||||
.map { it.transform() }
|
||||
availableItems = networks.availableNetworks
|
||||
.map { it.transform(true) }
|
||||
.toPersistentList(),
|
||||
notAvailableItems = networks.notAvailableNetworks
|
||||
.map { it.transform(false) }
|
||||
.toPersistentList(),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
private fun Network.transform(): NFTNetworkUM = NFTNetworkUM(
|
||||
id = id.value,
|
||||
iconRes = getActiveIconRes(id.value),
|
||||
name = name,
|
||||
onItemClick = {
|
||||
onNetworkClick(this)
|
||||
},
|
||||
)
|
||||
private fun Network.transform(enabled: Boolean): NFTNetworkUM {
|
||||
val custom = derivationPath is Network.DerivationPath.Custom
|
||||
return NFTNetworkUM(
|
||||
id = id.value + derivationPath.value,
|
||||
chainRowUM = ChainRowUM(
|
||||
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.model.Model
|
||||
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.ui.clipboard.ClipboardManager
|
||||
import com.tangem.core.ui.components.fields.InputManager
|
||||
import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
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.GetNFTAvailableNetworksUseCase
|
||||
import com.tangem.domain.nft.GetNFTNetworksUseCase
|
||||
import com.tangem.domain.nft.GetNFTNetworkStatusUseCase
|
||||
import com.tangem.domain.nft.analytics.NFTAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
|
@ -34,12 +36,13 @@ import javax.inject.Inject
|
|||
internal class NFTReceiveModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val searchManager: InputManager,
|
||||
private val getNFTAvailableNetworksUseCase: GetNFTAvailableNetworksUseCase,
|
||||
private val getNFTNetworksUseCase: GetNFTNetworksUseCase,
|
||||
private val filterNFTAvailableNetworksUseCase: FilterNFTAvailableNetworksUseCase,
|
||||
private val getNFTNetworkStatusUseCase: GetNFTNetworkStatusUseCase,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
private val shareManager: ShareManager,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val messageSender: UiMessageSender,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -53,7 +56,10 @@ internal class NFTReceiveModel @Inject constructor(
|
|||
formatArgs = wrappedList(params.walletName),
|
||||
),
|
||||
search = getInitialSearchBar(),
|
||||
networks = NFTReceiveUM.Networks.Content(persistentListOf()),
|
||||
networks = NFTReceiveUM.Networks.Content(
|
||||
availableItems = persistentListOf(),
|
||||
notAvailableItems = persistentListOf(),
|
||||
),
|
||||
bottomSheetConfig = null,
|
||||
),
|
||||
)
|
||||
|
|
@ -67,7 +73,7 @@ internal class NFTReceiveModel @Inject constructor(
|
|||
|
||||
private fun subscribeToNFTAvailableNetworks() {
|
||||
combine(
|
||||
flow = getNFTAvailableNetworksUseCase(params.userWalletId),
|
||||
flow = getNFTNetworksUseCase(params.userWalletId),
|
||||
flow2 = searchManager.query.distinctUntilChanged(),
|
||||
) { networks, query ->
|
||||
filterNFTAvailableNetworksUseCase(networks, query)
|
||||
|
|
@ -113,31 +119,40 @@ internal class NFTReceiveModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun onNetworkClick(network: Network) {
|
||||
modelScope.launch {
|
||||
analyticsEventHandler.send(NFTAnalyticsEvent.Receive.BlockchainChosen(network.name))
|
||||
private fun onNetworkClick(network: Network, enabled: Boolean) {
|
||||
if (!enabled) {
|
||||
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(
|
||||
userWalletId = params.userWalletId,
|
||||
network = network,
|
||||
) ?: return@launch
|
||||
messageSender.send(message)
|
||||
} else {
|
||||
modelScope.launch {
|
||||
analyticsEventHandler.send(NFTAnalyticsEvent.Receive.BlockchainChosen(network.name))
|
||||
|
||||
when (val value = networkStatus.value) {
|
||||
is NetworkStatus.Verified -> {
|
||||
_state.update {
|
||||
ShowReceiveBottomSheetTransformer(
|
||||
network = network,
|
||||
networkAddress = value.address,
|
||||
onDismissBottomSheet = ::onReceiveBottomSheetDismiss,
|
||||
onCopyClick = { text -> onCopyClick(text, network) },
|
||||
onShareClick = { text -> onShareClick(text, network) },
|
||||
).transform(it)
|
||||
val networkStatus = getNFTNetworkStatusUseCase.invoke(
|
||||
userWalletId = params.userWalletId,
|
||||
network = network,
|
||||
) ?: return@launch
|
||||
|
||||
when (val value = networkStatus.value) {
|
||||
is NetworkStatus.Verified -> {
|
||||
_state.update {
|
||||
ShowReceiveBottomSheetTransformer(
|
||||
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
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -51,18 +50,9 @@ internal fun NFTReceive(state: NFTReceiveUM, modifier: Modifier = Modifier) {
|
|||
colors = TangemSearchBarDefaults.secondaryTextFieldColors,
|
||||
)
|
||||
|
||||
val contentModifier = Modifier
|
||||
.padding(TangemTheme.dimens.spacing16)
|
||||
|
||||
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)
|
||||
}
|
||||
when (val networks = state.networks) {
|
||||
is NFTReceiveUM.Networks.Content -> NFTReceiveNetworksContent(networks)
|
||||
is NFTReceiveUM.Networks.Empty -> NFTReceiveNetworksEmpty()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,19 +1,29 @@
|
|||
package com.tangem.features.nft.receive.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.animateContentSize
|
||||
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.padding
|
||||
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.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.clip
|
||||
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.TangemThemePreview
|
||||
import com.tangem.features.nft.impl.R
|
||||
|
|
@ -27,40 +37,86 @@ internal fun NFTReceiveNetworksContent(state: NFTReceiveUM.Networks.Content, mod
|
|||
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.animateContentSize(),
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing8,
|
||||
),
|
||||
state = listState,
|
||||
contentPadding = PaddingValues(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
) {
|
||||
item(
|
||||
key = "title",
|
||||
) {
|
||||
Title()
|
||||
if (state.availableItems.isNotEmpty()) {
|
||||
item(
|
||||
key = "available_title",
|
||||
) {
|
||||
Title(resourceReference(R.string.nft_receive_available_section_title))
|
||||
}
|
||||
networks(state.availableItems)
|
||||
}
|
||||
items(
|
||||
items = state.items,
|
||||
key = NFTNetworkUM::id,
|
||||
) { network ->
|
||||
NFTNetwork(
|
||||
state = network,
|
||||
modifier = Modifier.animateItem(),
|
||||
)
|
||||
if (state.notAvailableItems.isNotEmpty()) {
|
||||
item(
|
||||
key = "not_available_title",
|
||||
) {
|
||||
Title(resourceReference(R.string.nft_receive_unavailable_section_title))
|
||||
}
|
||||
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
|
||||
private fun Title(modifier: Modifier = Modifier) {
|
||||
private fun Title(text: TextReference, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
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(
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
top = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing12,
|
||||
bottom = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
text = stringResourceSafe(R.string.nft_receive_choose_network),
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
|
|
@ -74,32 +130,84 @@ private fun Preview_NFTReceiveNetworksUM() {
|
|||
TangemThemePreview {
|
||||
NFTReceiveNetworksContent(
|
||||
state = NFTReceiveUM.Networks.Content(
|
||||
items = persistentListOf(
|
||||
availableItems = persistentListOf(
|
||||
NFTNetworkUM(
|
||||
id = "item1",
|
||||
name = "Ethereum",
|
||||
iconRes = R.drawable.img_eth_22,
|
||||
chainRowUM = ChainRowUM(
|
||||
name = "Ethereum",
|
||||
type = "",
|
||||
icon = CurrencyIconState.CoinIcon(
|
||||
url = null,
|
||||
fallbackResId = R.drawable.img_eth_22,
|
||||
isGrayscale = false,
|
||||
showCustomBadge = false,
|
||||
),
|
||||
showCustom = false,
|
||||
),
|
||||
onItemClick = { },
|
||||
),
|
||||
NFTNetworkUM(
|
||||
id = "item2",
|
||||
name = "Polygon",
|
||||
iconRes = R.drawable.img_polygon_22,
|
||||
chainRowUM = ChainRowUM(
|
||||
name = "Polygon",
|
||||
type = "",
|
||||
icon = CurrencyIconState.CoinIcon(
|
||||
url = null,
|
||||
fallbackResId = R.drawable.img_polygon_22,
|
||||
isGrayscale = false,
|
||||
showCustomBadge = false,
|
||||
),
|
||||
showCustom = false,
|
||||
),
|
||||
onItemClick = { },
|
||||
),
|
||||
NFTNetworkUM(
|
||||
id = "item3",
|
||||
name = "BSC",
|
||||
iconRes = R.drawable.img_bsc_22,
|
||||
chainRowUM = ChainRowUM(
|
||||
name = "BSC",
|
||||
type = "",
|
||||
icon = CurrencyIconState.CoinIcon(
|
||||
url = null,
|
||||
fallbackResId = R.drawable.img_bsc_22,
|
||||
isGrayscale = false,
|
||||
showCustomBadge = false,
|
||||
),
|
||||
showCustom = false,
|
||||
),
|
||||
onItemClick = { },
|
||||
),
|
||||
NFTNetworkUM(
|
||||
id = "item4",
|
||||
name = "Avalanche",
|
||||
iconRes = R.drawable.img_avalanche_22,
|
||||
chainRowUM = ChainRowUM(
|
||||
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 = { },
|
||||
),
|
||||
),
|
||||
notAvailableItems = persistentListOf(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.nft.receive.ui
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -16,6 +17,7 @@ internal fun NFTReceiveNetworksEmpty(modifier: Modifier = Modifier) {
|
|||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(TangemTheme.dimens.spacing16)
|
||||
.background(TangemTheme.colors.background.secondary),
|
||||
) {
|
||||
Text(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue