diff --git a/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt index 697065bb97..0578e95316 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt @@ -57,7 +57,7 @@ internal object NFTDomainModule { fun providesGetNFTAvailableNetworksUseCase( nftRepository: NFTRepository, currenciesRepository: CurrenciesRepository, - ): GetNFTAvailableNetworksUseCase = GetNFTAvailableNetworksUseCase( + ): GetNFTNetworksUseCase = GetNFTNetworksUseCase( currenciesRepository = currenciesRepository, nftRepository = nftRepository, ) diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index bda274d668..94ac2266d6 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -546,7 +546,6 @@ You haven\'t added this network yet. To start receiving NFTs, add it to the main screen! Network not added Not Added - Send NFT Traits %1$d NFTs in %2$d collection Tap here to receive first NFT diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt index 7b8f2c9fff..a9020f35ae 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt @@ -42,6 +42,7 @@ fun ChainRow(model: ChainRowUM, modifier: Modifier = Modifier, action: @Composab } else { null }, + isEnabled = model.enabled, accentMainText = true, accentSecondText = false, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/model/ChainRowUM.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/model/ChainRowUM.kt index c0c3764c5e..5b5f0ebd21 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/model/ChainRowUM.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/model/ChainRowUM.kt @@ -9,4 +9,5 @@ data class ChainRowUM( val type: String, val icon: CurrencyIconState, val showCustom: Boolean, + val enabled: Boolean = true, ) \ 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 7fe4317d15..b19fef322d 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 @@ -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() @@ -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 { + 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, diff --git a/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTNetworks.kt b/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTNetworks.kt new file mode 100644 index 0000000000..14cb57f3e7 --- /dev/null +++ b/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTNetworks.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.nft.models + +import com.tangem.domain.tokens.model.Network + +data class NFTNetworks( + val availableNetworks: List, + val notAvailableNetworks: List, +) \ No newline at end of file diff --git a/domain/nft/src/main/kotlin/com/tangem/domain/nft/FilterNFTAvailableNetworksUseCase.kt b/domain/nft/src/main/kotlin/com/tangem/domain/nft/FilterNFTAvailableNetworksUseCase.kt index 3308232789..ca8aff4b42 100644 --- a/domain/nft/src/main/kotlin/com/tangem/domain/nft/FilterNFTAvailableNetworksUseCase.kt +++ b/domain/nft/src/main/kotlin/com/tangem/domain/nft/FilterNFTAvailableNetworksUseCase.kt @@ -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, searchQuery: String): List = + 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.filter(searchQuery: String) = filter { + it.name.contains(searchQuery, ignoreCase = true) || + it.currencySymbol.contains(searchQuery, ignoreCase = true) + } } \ No newline at end of file diff --git a/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTAvailableNetworksUseCase.kt b/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTAvailableNetworksUseCase.kt deleted file mode 100644 index 203f12e012..0000000000 --- a/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTAvailableNetworksUseCase.kt +++ /dev/null @@ -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> = currenciesRepository - .getWalletCurrenciesUpdates(userWalletId) - .map { cryptoCurrencies -> - cryptoCurrencies - .map { cryptoCurrency -> cryptoCurrency.network } - .distinct() - .filter { nftRepository.isNFTSupported(it) } - } -} \ No newline at end of file diff --git a/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTNetworksUseCase.kt b/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTNetworksUseCase.kt new file mode 100644 index 0000000000..ca8a8c22bd --- /dev/null +++ b/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTNetworksUseCase.kt @@ -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 = 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, + ) + } +} \ No newline at end of file diff --git a/domain/nft/src/main/kotlin/com/tangem/domain/nft/repository/NFTRepository.kt b/domain/nft/src/main/kotlin/com/tangem/domain/nft/repository/NFTRepository.kt index ea99cbca2a..1484b241a3 100644 --- a/domain/nft/src/main/kotlin/com/tangem/domain/nft/repository/NFTRepository.kt +++ b/domain/nft/src/main/kotlin/com/tangem/domain/nft/repository/NFTRepository.kt @@ -18,5 +18,7 @@ interface NFTRepository { suspend fun isNFTSupported(network: Network): Boolean + suspend fun getNFTSupportedNetworks(userWalletId: UserWalletId): List + suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String? } \ No newline at end of file diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/NFTNetworkUM.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/NFTNetworkUM.kt index 0ef73aa689..dc3c6ae968 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/NFTNetworkUM.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/NFTNetworkUM.kt @@ -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, ) \ No newline at end of file diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/NFTReceiveUM.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/NFTReceiveUM.kt index e31c80c425..eea9a23b79 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/NFTReceiveUM.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/NFTReceiveUM.kt @@ -19,7 +19,8 @@ internal data class NFTReceiveUM( data object Empty : Networks() data class Content( - val items: ImmutableList, + val availableItems: ImmutableList, + val notAvailableItems: ImmutableList, ) : Networks() } } \ No newline at end of file diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/transformer/UpdateDataStateTransformer.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/transformer/UpdateDataStateTransformer.kt index 560bf018f7..c113d54905 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/transformer/UpdateDataStateTransformer.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/transformer/UpdateDataStateTransformer.kt @@ -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, - private val onNetworkClick: (Network) -> Unit, + private val networks: NFTNetworks, + private val onNetworkClick: (Network, Boolean) -> Unit, ) : Transformer { 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) + }, + ) + } } \ No newline at end of file diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt index 88a9b57851..090d51ff5c 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt @@ -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 } } } diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTNetwork.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTNetwork.kt deleted file mode 100644 index 9b60320b49..0000000000 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTNetwork.kt +++ /dev/null @@ -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( - collection = listOf( - NFTNetworkUM( - id = "item1", - name = "Nethers #0853", - iconRes = R.drawable.img_eth_22, - onItemClick = { }, - ), - ), -) \ No newline at end of file diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt index 47a338514f..4dac3a8d5c 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt @@ -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() } } }, diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceiveNetworksContent.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceiveNetworksContent.kt index 23fab1ef15..8b01fd73b0 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceiveNetworksContent.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceiveNetworksContent.kt @@ -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) { + 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(), ), ) } diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceiveNetworksEmpty.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceiveNetworksEmpty.kt index 931138a97d..368e7fd8fc 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceiveNetworksEmpty.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceiveNetworksEmpty.kt @@ -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(