diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 3c0ac636bb..31026d4c57 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -420,7 +420,11 @@ internal class ChildFactory @Inject constructor( is AppRoute.NFTDetails -> createComponentChild( context = context, - params = NFTDetailsComponent.Params(userWalletId = route.userWalletId, nftAsset = route.nftAsset), + params = NFTDetailsComponent.Params( + userWalletId = route.userWalletId, + nftAsset = route.nftAsset, + nftCollectionName = route.collectionName, + ), componentFactory = nftDetailsComponentFactory, ) is AppRoute.OnboardingNote, @@ -772,7 +776,11 @@ internal class ChildFactory @Inject constructor( is AppRoute.NFTDetails -> route.asComponentChild( contextProvider = contextProvider(route, contextFactory), - params = NFTDetailsComponent.Params(userWalletId = route.userWalletId, nftAsset = route.nftAsset), + params = NFTDetailsComponent.Params( + userWalletId = route.userWalletId, + nftAsset = route.nftAsset, + nftCollectionName = route.collectionName, + ), componentFactory = nftDetailsComponentFactory, ) } diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index cc4bda0a08..2c697c52b9 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -294,5 +294,6 @@ sealed class AppRoute(val path: String) : Route { data class NFTDetails( val userWalletId: UserWalletId, val nftAsset: NFTAsset, + val collectionName: String, ) : AppRoute(path = "/nft_details/${userWalletId.stringValue}/${nftAsset.collectionId}/${nftAsset.id.stringValue}") } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconTopBadge.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconTopBadge.kt index 03057d495d..f7020ce9e4 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconTopBadge.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconTopBadge.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import com.tangem.core.ui.res.TangemTheme @@ -19,12 +20,13 @@ fun CurrencyIconTopBadge( alpha: Float, colorFilter: ColorFilter?, modifier: Modifier = Modifier, + background: Color = TangemTheme.colors.background.primary, ) { Box( modifier = modifier .size(TangemTheme.dimens.size18) .background( - color = TangemTheme.colors.background.primary, + color = background, shape = CircleShape, ), ) { diff --git a/features/nft/api/src/main/kotlin/com/tangem/features/nft/component/NFTDetailsBlockComponent.kt b/features/nft/api/src/main/kotlin/com/tangem/features/nft/component/NFTDetailsBlockComponent.kt new file mode 100644 index 0000000000..676f626597 --- /dev/null +++ b/features/nft/api/src/main/kotlin/com/tangem/features/nft/component/NFTDetailsBlockComponent.kt @@ -0,0 +1,17 @@ +package com.tangem.features.nft.component + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.nft.models.NFTAsset +import com.tangem.domain.wallets.models.UserWalletId + +interface NFTDetailsBlockComponent : ComposableContentComponent { + + data class Params( + val userWalletId: UserWalletId, + val nftAsset: NFTAsset, + val nftCollectionName: String, + ) + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/nft/api/src/main/kotlin/com/tangem/features/nft/component/NFTDetailsComponent.kt b/features/nft/api/src/main/kotlin/com/tangem/features/nft/component/NFTDetailsComponent.kt index 0f43e3a58f..2848de0dd5 100644 --- a/features/nft/api/src/main/kotlin/com/tangem/features/nft/component/NFTDetailsComponent.kt +++ b/features/nft/api/src/main/kotlin/com/tangem/features/nft/component/NFTDetailsComponent.kt @@ -10,6 +10,7 @@ interface NFTDetailsComponent : ComposableContentComponent { data class Params( val userWalletId: UserWalletId, val nftAsset: NFTAsset, + val nftCollectionName: String, ) interface Factory : ComponentFactory diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt index e5fe0846c9..c5eec23071 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt @@ -20,7 +20,7 @@ internal class UpdateDataStateTransformer( private val onRetryClick: () -> Unit, private val onExpandCollectionClick: (NFTCollection) -> Unit, private val onRetryAssetsClick: (NFTCollection) -> Unit, - private val onAssetClick: (NFTAsset) -> Unit, + private val onAssetClick: (NFTAsset, String) -> Unit, private val initialSearchBarFactory: () -> SearchBarUM, ) : Transformer { @@ -105,12 +105,12 @@ internal class UpdateDataStateTransformer( is NFTCollection.Assets.Value -> NFTCollectionAssetsListUM.Content( items = assets .items - .map { it.transform() } + .map { it.transform(name.orEmpty()) } .toPersistentList(), ) } - private fun NFTAsset.transform(): NFTCollectionAssetUM = NFTCollectionAssetUM( + private fun NFTAsset.transform(collectionName: String): NFTCollectionAssetUM = NFTCollectionAssetUM( id = id.toString(), name = name.orEmpty(), imageUrl = media?.url, @@ -121,7 +121,7 @@ internal class UpdateDataStateTransformer( is NFTSalePrice.Value -> NFTSalePriceUM.Content(salePrice.value.toString()) }, onItemClick = { - onAssetClick(this) + onAssetClick(this, collectionName) }, ) diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt index 21618350db..b8b89b6d9b 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt @@ -115,11 +115,12 @@ internal class NFTCollectionsModel @Inject constructor( // TODO refresh all } - private fun onAssetClick(asset: NFTAsset) { + private fun onAssetClick(asset: NFTAsset, collectionName: String) { router.push( AppRoute.NFTDetails( userWalletId = params.userWalletId, nftAsset = asset, + collectionName = collectionName, ), ) } diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollection.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollection.kt index ffaf0c94b1..e89b5bbe41 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollection.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollection.kt @@ -2,7 +2,6 @@ package com.tangem.features.nft.collections.ui import android.content.res.Configuration import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material3.Icon @@ -11,25 +10,19 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext 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 coil.compose.SubcomposeAsyncImage -import coil.request.ImageRequest -import com.tangem.core.ui.components.RectangleShimmer -import com.tangem.core.ui.components.currency.icon.CurrencyIconTopBadge import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.nft.collections.entity.NFTCollectionAssetsListUM import com.tangem.features.nft.collections.entity.NFTCollectionUM +import com.tangem.features.nft.common.ui.NFTLogo import com.tangem.features.nft.impl.R import kotlinx.collections.immutable.persistentListOf @@ -52,7 +45,11 @@ internal fun NFTCollection(state: NFTCollectionUM, modifier: Modifier = Modifier ), verticalAlignment = Alignment.CenterVertically, ) { - Logo(state) + NFTLogo( + imageUrl = state.logoUrl, + networkIconId = state.networkIconId, + background = TangemTheme.colors.background.primary, + ) Text(state) @@ -81,46 +78,6 @@ internal fun NFTCollection(state: NFTCollectionUM, modifier: Modifier = Modifier } } -@Composable -private fun Logo(state: NFTCollectionUM) { - val networkBadgeOffset = TangemTheme.dimens.spacing6 - - Box( - modifier = Modifier, - ) { - SubcomposeAsyncImage( - modifier = Modifier - .align(Alignment.CenterStart) - .size(TangemTheme.dimens.size36) - .clip(TangemTheme.shapes.roundedCorners8), - model = ImageRequest.Builder(LocalContext.current) - .data(state.logoUrl) - .crossfade(true) - .build(), - loading = { - RectangleShimmer(radius = TangemTheme.dimens.radius8) - }, - error = { - Box( - modifier = Modifier - .clip(shape = TangemTheme.shapes.roundedCorners8) - .background(TangemTheme.colors.field.primary), - ) - }, - contentScale = ContentScale.Crop, - contentDescription = null, - ) - CurrencyIconTopBadge( - modifier = Modifier - .offset(x = networkBadgeOffset, y = -networkBadgeOffset) - .align(Alignment.TopEnd), - iconResId = state.networkIconId, - alpha = 1f, - colorFilter = null, - ) - } -} - @Composable private fun RowScope.Text(state: NFTCollectionUM) { Column( diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/ui/NFTLogo.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/ui/NFTLogo.kt new file mode 100644 index 0000000000..db5f6d01ca --- /dev/null +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/ui/NFTLogo.kt @@ -0,0 +1,60 @@ +package com.tangem.features.nft.common.ui + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +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.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import coil.compose.SubcomposeAsyncImage +import coil.request.ImageRequest +import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.components.currency.icon.CurrencyIconTopBadge +import com.tangem.core.ui.res.TangemTheme + +@Composable +internal fun NFTLogo(imageUrl: String?, @DrawableRes networkIconId: Int, background: Color = Color.Transparent) { + val networkBadgeOffset = TangemTheme.dimens.spacing6 + + Box( + modifier = Modifier, + ) { + SubcomposeAsyncImage( + modifier = Modifier + .align(Alignment.CenterStart) + .size(TangemTheme.dimens.size36) + .clip(TangemTheme.shapes.roundedCorners8), + model = ImageRequest.Builder(LocalContext.current) + .data(imageUrl) + .crossfade(true) + .build(), + loading = { + RectangleShimmer(radius = TangemTheme.dimens.radius8) + }, + error = { + Box( + modifier = Modifier + .clip(shape = TangemTheme.shapes.roundedCorners8) + .background(TangemTheme.colors.field.primary), + ) + }, + contentScale = ContentScale.Crop, + contentDescription = null, + ) + CurrencyIconTopBadge( + modifier = Modifier + .offset(x = networkBadgeOffset, y = -networkBadgeOffset) + .align(Alignment.TopEnd), + iconResId = networkIconId, + alpha = 1f, + colorFilter = null, + background = background, + ) + } +} \ No newline at end of file diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/block/DefaultNFTDetailsBlockComponent.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/block/DefaultNFTDetailsBlockComponent.kt new file mode 100644 index 0000000000..1c20de2872 --- /dev/null +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/block/DefaultNFTDetailsBlockComponent.kt @@ -0,0 +1,36 @@ +package com.tangem.features.nft.details.block + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.ui.extensions.getActiveIconRes +import com.tangem.core.ui.extensions.stringReference +import com.tangem.features.nft.component.NFTDetailsBlockComponent +import com.tangem.features.nft.details.block.ui.NFTDetailsBlock +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +class DefaultNFTDetailsBlockComponent @AssistedInject constructor( + @Assisted context: AppComponentContext, + @Assisted private val params: NFTDetailsBlockComponent.Params, +) : NFTDetailsBlockComponent, AppComponentContext by context { + + @Composable + override fun Content(modifier: Modifier) { + NFTDetailsBlock( + assetName = stringReference(params.nftAsset.name.orEmpty()), + collectionName = stringReference(params.nftCollectionName), + assetImage = params.nftAsset.media?.url, + networkIconRes = getActiveIconRes(params.nftAsset.network.id.value), + ) + } + + @AssistedFactory + interface Factory : NFTDetailsBlockComponent.Factory { + override fun create( + context: AppComponentContext, + params: NFTDetailsBlockComponent.Params, + ): DefaultNFTDetailsBlockComponent + } +} \ No newline at end of file diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/block/ui/NFTDetailsBlock.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/block/ui/NFTDetailsBlock.kt new file mode 100644 index 0000000000..6336d2dbb2 --- /dev/null +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/block/ui/NFTDetailsBlock.kt @@ -0,0 +1,84 @@ +package com.tangem.features.nft.details.block.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +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.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.nft.common.ui.NFTLogo +import com.tangem.features.nft.impl.R + +@Composable +internal fun NFTDetailsBlock( + assetName: TextReference, + collectionName: TextReference, + assetImage: String?, + networkIconRes: Int, +) { + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)) + .background(TangemTheme.colors.background.action) + .padding(12.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + Text( + text = "NFT Asset", + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.secondary, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + NFTLogo( + assetImage, + networkIconRes, + background = TangemTheme.colors.background.action, + ) + + Column( + verticalArrangement = Arrangement.spacedBy(2.dp), + ) { + Text( + text = assetName.resolveReference(), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.primary1, + ) + Text( + text = collectionName.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + } + } +} + +// region Preview +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun NFTDetailsBlock_Preview() { + TangemThemePreview { + NFTDetailsBlock( + assetName = stringReference("NFT Asset Name"), + collectionName = stringReference("NFT Collection"), + assetImage = null, + networkIconRes = R.drawable.img_polygon_22, + ) + } +} +// endregion \ No newline at end of file diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/model/NFTDetailsModel.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/model/NFTDetailsModel.kt index e421efcde8..77fc4b7e96 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/model/NFTDetailsModel.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/model/NFTDetailsModel.kt @@ -9,7 +9,8 @@ import com.tangem.features.nft.details.entity.NFTAssetUM import com.tangem.features.nft.details.entity.NFTDetailsUM import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import javax.inject.Inject @ModelScoped @@ -56,6 +57,13 @@ internal class NFTDetailsModel @Inject constructor( private fun onSendClick() { // TODO implement + // router.push( + // AppRoute.NFTSend( + // userWalletId = params.userWalletId, + // nftAsset = params.nftAsset, + // nftCollectionName = params.nftCollectionName, + // ), + // ) } private fun navigateBack() { diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/NFTFeatureModule.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/di/NFTFeatureModule.kt similarity index 82% rename from features/nft/impl/src/main/kotlin/com/tangem/features/nft/NFTFeatureModule.kt rename to features/nft/impl/src/main/kotlin/com/tangem/features/nft/di/NFTFeatureModule.kt index 75f083f217..0d8889e2e8 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/NFTFeatureModule.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/di/NFTFeatureModule.kt @@ -1,13 +1,17 @@ -package com.tangem.features.nft +package com.tangem.features.nft.di import com.tangem.core.configtoggle.feature.FeatureTogglesManager import com.tangem.core.decompose.model.Model +import com.tangem.features.nft.DefaultNFTFeatureToggles +import com.tangem.features.nft.NFTFeatureToggles import com.tangem.features.nft.collections.DefaultNFTCollectionsComponent import com.tangem.features.nft.collections.model.NFTCollectionsModel import com.tangem.features.nft.component.NFTCollectionsComponent +import com.tangem.features.nft.component.NFTDetailsBlockComponent import com.tangem.features.nft.component.NFTDetailsComponent import com.tangem.features.nft.component.NFTReceiveComponent import com.tangem.features.nft.details.DefaultNFTDetailsComponent +import com.tangem.features.nft.details.block.DefaultNFTDetailsBlockComponent import com.tangem.features.nft.details.model.NFTDetailsModel import com.tangem.features.nft.receive.DefaultNFTReceiveComponent import com.tangem.features.nft.receive.model.NFTReceiveModel @@ -62,4 +66,10 @@ internal interface NFTFeatureModuleBinds { @IntoMap @ClassKey(NFTDetailsModel::class) fun bindNFTDetailsModel(model: NFTDetailsModel): Model + + @Binds + @Singleton + fun bindNFTDetailsBlockComponentFactory( + impl: DefaultNFTDetailsBlockComponent.Factory, + ): NFTDetailsBlockComponent.Factory } \ No newline at end of file