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 dcedc27851..4d82335720 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
@@ -154,4 +154,10 @@ internal object NFTDomainModule {
): ObserveAndClearNFTCacheIfNeedUseCase {
return ObserveAndClearNFTCacheIfNeedUseCase(nftRepository, currenciesRepository)
}
+
+ @Provides
+ @Singleton
+ fun provideGetNftCurrencyUseCase(nftRepository: NFTRepository): GetNFTCurrencyUseCase {
+ return GetNFTCurrencyUseCase(nftRepository)
+ }
}
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt
index ff58fd1c80..1d4ff127d6 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt
@@ -273,6 +273,7 @@ sealed class NotificationUM(val config: NotificationConfig) {
iconResId: Int = R.drawable.ic_alert_circle_24,
buttonsState: NotificationConfig.ButtonsState? = null,
onCloseClick: (() -> Unit)? = null,
+ iconTint: NotificationConfig.IconTint = NotificationConfig.IconTint.Unspecified,
) : NotificationUM(
config = NotificationConfig(
title = title,
@@ -280,6 +281,7 @@ sealed class NotificationUM(val config: NotificationConfig) {
iconResId = iconResId,
buttonsState = buttonsState,
onCloseClick = onCloseClick,
+ iconTint = iconTint,
),
)
diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
index 6ecb2f5ac6..1158354430 100644
--- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
+++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
@@ -50,5 +50,9 @@
{
"name": "HOT_WALLET_ENABLED",
"version": "undefined"
+ },
+ {
+ "name": "NEW_TOKEN_RECEIVE_ENABLED",
+ "version": "undefined"
}
]
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt
index 4727ba9759..ff97530009 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt
@@ -13,6 +13,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.CircleShimmer
import com.tangem.core.ui.res.TangemTheme
@@ -28,11 +29,16 @@ import com.tangem.core.ui.utils.getGreyScaleColorFilter
* @param shouldDisplayNetwork specifies whether to display network badge
*/
@Composable
-fun CurrencyIcon(state: CurrencyIconState, modifier: Modifier = Modifier, shouldDisplayNetwork: Boolean = true) {
+fun CurrencyIcon(
+ state: CurrencyIconState,
+ modifier: Modifier = Modifier,
+ shouldDisplayNetwork: Boolean = true,
+ iconSize: Dp = 36.dp,
+) {
BaseContainer(modifier = modifier) {
val iconModifier = Modifier
.align(Alignment.Center)
- .size(TangemTheme.dimens.size36)
+ .size(iconSize)
when (state) {
is CurrencyIconState.Loading -> LoadingIcon(modifier = iconModifier)
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconStateBuilder.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconStateBuilder.kt
new file mode 100644
index 0000000000..1aaed64244
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconStateBuilder.kt
@@ -0,0 +1,103 @@
+package com.tangem.core.ui.components.currency.icon
+
+import androidx.annotation.DrawableRes
+import androidx.compose.ui.graphics.Color
+import com.tangem.core.ui.R
+import com.tangem.core.ui.extensions.getTintForTokenIcon
+import com.tangem.core.ui.extensions.networkIconResId
+import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon
+import com.tangem.domain.models.currency.CryptoCurrency
+
+object CurrencyIconStateBuilder {
+
+ fun build(
+ cryptoCurrency: CryptoCurrency,
+ isGrayscale: Boolean = false,
+ showCustomBadge: Boolean = true,
+ ): CurrencyIconState = when (cryptoCurrency) {
+ is CryptoCurrency.Coin -> fromCoin(cryptoCurrency, isGrayscale, showCustomBadge)
+ is CryptoCurrency.Token -> fromToken(cryptoCurrency, isGrayscale, showCustomBadge)
+ }
+
+ private fun createCoinIcon(
+ url: String? = null,
+ @DrawableRes fallbackResId: Int = R.drawable.ic_empty_64,
+ isGrayscale: Boolean = false,
+ showCustomBadge: Boolean = false,
+ ): CurrencyIconState.CoinIcon = CurrencyIconState.CoinIcon(
+ url = url,
+ fallbackResId = fallbackResId,
+ isGrayscale = isGrayscale,
+ showCustomBadge = showCustomBadge,
+ )
+
+ private fun createTokenIcon(
+ url: String? = null,
+ @DrawableRes topBadgeIconResId: Int? = null,
+ isGrayscale: Boolean = false,
+ showCustomBadge: Boolean = false,
+ fallbackTint: Color = Color.Black,
+ fallbackBackground: Color = Color.White,
+ ): CurrencyIconState.TokenIcon = CurrencyIconState.TokenIcon(
+ url = url,
+ topBadgeIconResId = topBadgeIconResId,
+ isGrayscale = isGrayscale,
+ fallbackTint = fallbackTint,
+ fallbackBackground = fallbackBackground,
+ showCustomBadge = showCustomBadge,
+ )
+
+ private fun createCustomTokenIcon(
+ tint: Color,
+ background: Color,
+ @DrawableRes topBadgeIconResId: Int,
+ isGrayscale: Boolean = false,
+ showCustomBadge: Boolean = true,
+ ): CurrencyIconState.CustomTokenIcon = CurrencyIconState.CustomTokenIcon(
+ tint = tint,
+ background = background,
+ topBadgeIconResId = topBadgeIconResId,
+ isGrayscale = isGrayscale,
+ showCustomBadge = showCustomBadge,
+ )
+
+ private fun fromCoin(
+ coin: CryptoCurrency.Coin,
+ isGrayscale: Boolean = false,
+ showCustomBadge: Boolean = true,
+ ): CurrencyIconState.CoinIcon = createCoinIcon(
+ url = coin.iconUrl,
+ fallbackResId = coin.networkIconResId,
+ isGrayscale = isGrayscale || coin.network.isTestnet,
+ showCustomBadge = coin.isCustom && showCustomBadge,
+ )
+
+ private fun fromToken(
+ token: CryptoCurrency.Token,
+ isGrayscale: Boolean = false,
+ showCustomBadge: Boolean = true,
+ ): CurrencyIconState {
+ val grayScale = isGrayscale || token.network.isTestnet
+ val background = token.tryGetBackgroundForTokenIcon(grayScale)
+ val tint = getTintForTokenIcon(background)
+
+ return if (token.isCustom && token.iconUrl == null) {
+ createCustomTokenIcon(
+ tint = tint,
+ background = background,
+ topBadgeIconResId = token.networkIconResId,
+ isGrayscale = grayScale,
+ showCustomBadge = showCustomBadge,
+ )
+ } else {
+ createTokenIcon(
+ url = token.iconUrl,
+ topBadgeIconResId = token.networkIconResId,
+ isGrayscale = grayScale,
+ fallbackTint = tint,
+ fallbackBackground = background,
+ showCustomBadge = token.isCustom && showCustomBadge,
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/ui/src/main/res/drawable/ic_copy_new_24.xml b/core/ui/src/main/res/drawable/ic_copy_new_24.xml
new file mode 100644
index 0000000000..a073a2b686
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_copy_new_24.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/core/ui/src/main/res/drawable/ic_ens_36.xml b/core/ui/src/main/res/drawable/ic_ens_36.xml
new file mode 100644
index 0000000000..29b19ea2c7
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_ens_36.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/ui/src/main/res/drawable/ic_qrcode_new_24.xml b/core/ui/src/main/res/drawable/ic_qrcode_new_24.xml
new file mode 100644
index 0000000000..173a8c1979
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_qrcode_new_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt
new file mode 100644
index 0000000000..728496ef05
--- /dev/null
+++ b/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt
@@ -0,0 +1,37 @@
+package com.tangem.domain.models
+
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.wallet.UserWalletId
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class TokenReceiveConfig(
+ val shouldShowWarning: Boolean,
+ val cryptoCurrency: CryptoCurrency,
+ val userWalletId: UserWalletId,
+ val showMemoDisclaimer: Boolean,
+ val receiveAddress: List,
+ val tokenReceiveNotification: List = emptyList(),
+ val asset: Asset = Asset.Currency,
+)
+
+@Serializable
+data class ReceiveAddressModel(
+ val nameService: NameService,
+ val value: String,
+ val displayName: String,
+) {
+ enum class NameService {
+ Default, Ens
+ }
+}
+
+@Serializable
+data class TokenReceiveNotification(
+ val title: Int,
+ val subtitle: Int,
+)
+
+enum class Asset {
+ Currency, NFT
+}
\ No newline at end of file
diff --git a/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTCurrencyUseCase.kt b/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTCurrencyUseCase.kt
new file mode 100644
index 0000000000..85f77d810a
--- /dev/null
+++ b/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTCurrencyUseCase.kt
@@ -0,0 +1,10 @@
+package com.tangem.domain.nft
+
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.network.Network
+import com.tangem.domain.nft.repository.NFTRepository
+
+class GetNFTCurrencyUseCase(private val nftRepository: NFTRepository) {
+
+ operator fun invoke(network: Network): CryptoCurrency = nftRepository.getNFTCurrency(network)
+}
\ No newline at end of file
diff --git a/features/token-recieve/api/src/main/kotlin/com/tangem/features/tokenreceive/TokenReceiveFeatureToggle.kt b/features/token-recieve/api/src/main/kotlin/com/tangem/features/tokenreceive/TokenReceiveFeatureToggle.kt
new file mode 100644
index 0000000000..62175072dd
--- /dev/null
+++ b/features/token-recieve/api/src/main/kotlin/com/tangem/features/tokenreceive/TokenReceiveFeatureToggle.kt
@@ -0,0 +1,6 @@
+package com.tangem.features.tokenreceive
+
+interface TokenReceiveFeatureToggle {
+
+ val isNewTokenReceiveEnabled: Boolean
+}
\ No newline at end of file
diff --git a/features/token-recieve/impl/build.gradle.kts b/features/token-recieve/impl/build.gradle.kts
index d46cd33aad..dfd0d93ada 100644
--- a/features/token-recieve/impl/build.gradle.kts
+++ b/features/token-recieve/impl/build.gradle.kts
@@ -44,11 +44,13 @@ dependencies {
implementation(projects.core.analytics.models)
implementation(projects.core.decompose)
implementation(projects.core.res)
+ implementation(projects.core.configToggles)
/** Domain modules */
implementation(projects.domain.models)
implementation(projects.domain.transaction)
implementation(projects.domain.transaction.models)
+ implementation(projects.domain.tokens)
/** Feature Apis */
implementation(projects.features.tokenRecieve.api)
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/DefaultTokenReceiveFeatureToggle.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/DefaultTokenReceiveFeatureToggle.kt
new file mode 100644
index 0000000000..209317d97b
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/DefaultTokenReceiveFeatureToggle.kt
@@ -0,0 +1,11 @@
+package com.tangem.features.tokenreceive
+
+import com.tangem.core.configtoggle.feature.FeatureTogglesManager
+
+internal class DefaultTokenReceiveFeatureToggle(
+ private val featureTogglesManager: FeatureTogglesManager,
+) : TokenReceiveFeatureToggle {
+
+ override val isNewTokenReceiveEnabled: Boolean
+ get() = featureTogglesManager.isFeatureEnabled("NEW_TOKEN_RECEIVE_ENABLED")
+}
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/ReceiveAddress.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/ReceiveAddress.kt
new file mode 100644
index 0000000000..960dda1224
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/ReceiveAddress.kt
@@ -0,0 +1,16 @@
+package com.tangem.features.tokenreceive.entity
+
+import com.tangem.core.ui.extensions.TextReference
+
+internal data class ReceiveAddress(
+ val value: String,
+ val type: Type,
+) {
+ sealed interface Type {
+ data object Ens : Type
+
+ data class Default(
+ val displayName: TextReference,
+ ) : Type
+ }
+}
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/ContainerWithSnackbarHost.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/ContainerWithSnackbarHost.kt
new file mode 100644
index 0000000000..94415b927d
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/ContainerWithSnackbarHost.kt
@@ -0,0 +1,23 @@
+package com.tangem.features.tokenreceive.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.tangem.core.ui.components.snackbar.CopiedTextSnackbarHost
+
+@Composable
+internal fun ContainerWithSnackbarHost(snackbarHostState: SnackbarHostState, content: @Composable () -> Unit) {
+ Box {
+ content()
+ CopiedTextSnackbarHost(
+ hostState = snackbarHostState,
+ modifier = Modifier
+ .align(Alignment.BottomCenter)
+ .padding(bottom = 80.dp),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContent.kt
new file mode 100644
index 0000000000..10f41e7e05
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContent.kt
@@ -0,0 +1,346 @@
+package com.tangem.features.tokenreceive.ui
+
+import android.content.res.Configuration
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.key
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
+import com.tangem.common.ui.notifications.NotificationUM
+import com.tangem.core.res.getStringSafe
+import com.tangem.core.ui.R
+import com.tangem.core.ui.components.SpacerH12
+import com.tangem.core.ui.components.SpacerH8
+import com.tangem.core.ui.components.SpacerW12
+import com.tangem.core.ui.components.SpacerW8
+import com.tangem.core.ui.components.atoms.text.EllipsisText
+import com.tangem.core.ui.components.atoms.text.TextEllipsis
+import com.tangem.core.ui.components.buttons.small.TangemIconButton
+import com.tangem.core.ui.components.icons.identicon.IdentIcon
+import com.tangem.core.ui.components.notifications.Notification
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.stringReference
+import com.tangem.core.ui.extensions.stringResourceSafe
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
+import com.tangem.features.tokenreceive.entity.ReceiveAddress
+import com.tangem.features.tokenreceive.ui.state.ReceiveAssetsUM
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.ImmutableMap
+import kotlinx.collections.immutable.persistentListOf
+import kotlinx.collections.immutable.persistentMapOf
+import kotlinx.coroutines.launch
+
+@Composable
+internal fun TokenReceiveAssetsContent(assetsUM: ReceiveAssetsUM) {
+ val snackbarHostState = remember(::SnackbarHostState)
+
+ ContainerWithSnackbarHost(snackbarHostState = snackbarHostState) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(color = TangemTheme.colors.background.tertiary)
+ .padding(
+ start = 16.dp,
+ end = 16.dp,
+ bottom = 8.dp,
+ ),
+ ) {
+ SpacerH8()
+
+ Info(
+ showMemoDisclaimer = assetsUM.showMemoDisclaimer,
+ notificationConfigs = assetsUM.notificationConfigs,
+ )
+
+ SpacerH12()
+
+ AddressBlock(
+ onCopyClick = assetsUM.onCopyClick,
+ onOpenQrCodeClick = assetsUM.onOpenQrCodeClick,
+ addresses = assetsUM.addresses,
+ snackbarHostState = snackbarHostState,
+ fullName = assetsUM.fullName,
+ )
+
+ if (assetsUM.isEnsResultLoading) {
+ LoadingBlock()
+ }
+ }
+ }
+}
+
+@Composable
+private fun Info(
+ notificationConfigs: ImmutableList,
+ showMemoDisclaimer: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(space = 16.dp),
+ ) {
+ if (showMemoDisclaimer) {
+ Text(
+ modifier = Modifier
+ .padding(horizontal = 18.dp)
+ .fillMaxWidth(),
+ text = stringResourceSafe(R.string.receive_bottom_sheet_no_memo_required_message),
+ style = TangemTheme.typography.caption1,
+ color = TangemTheme.colors.text.tertiary,
+ textAlign = TextAlign.Center,
+ )
+ }
+
+ notificationConfigs.fastForEach {
+ key(it.hashCode()) {
+ Notification(config = it.config)
+ }
+ }
+ }
+}
+
+@Composable
+private fun AddressBlock(
+ onOpenQrCodeClick: (id: Int) -> Unit,
+ onCopyClick: (id: Int) -> Unit,
+ addresses: ImmutableMap,
+ snackbarHostState: SnackbarHostState,
+ fullName: String,
+) {
+ val hapticFeedback = LocalHapticFeedback.current
+ val coroutineScope = rememberCoroutineScope()
+ val context = LocalContext.current
+ val resources = context.resources
+
+ addresses.entries.toList().fastForEach { entry ->
+ when (entry.value.type) {
+ is ReceiveAddress.Type.Default -> {
+ key(entry.key) {
+ AddressItem(
+ onCopyClick = {
+ onCopyClick(entry.key)
+ hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+ coroutineScope.launch {
+ snackbarHostState.showSnackbar(
+ message = resources.getStringSafe(
+ R.string.wallet_notification_address_copied,
+ ),
+ )
+ }
+ },
+ onOpenQrCodeClick = {
+ hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+ onOpenQrCodeClick(entry.key)
+ },
+ fullName = fullName,
+ address = entry.value.value,
+ )
+ SpacerH8()
+ }
+ }
+ ReceiveAddress.Type.Ens -> {
+ key(entry.key) {
+ EnsItem(
+ onCopyClick = {
+ onCopyClick(entry.key)
+ hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+ coroutineScope.launch {
+ snackbarHostState.showSnackbar(
+ message = resources.getStringSafe(
+ R.string.wallet_notification_address_copied,
+ ),
+ )
+ }
+ },
+ address = entry.value.value,
+ )
+ SpacerH8()
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun AddressItem(
+ onOpenQrCodeClick: () -> Unit,
+ fullName: String,
+ onCopyClick: () -> Unit,
+ address: String,
+ modifier: Modifier = Modifier,
+) {
+ Card(
+ modifier = modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(containerColor = TangemTheme.colors.background.action),
+ onClick = onOpenQrCodeClick,
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 14.dp, horizontal = 12.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ IdentIcon(
+ address = address,
+ modifier = Modifier
+ .size(size = 36.dp)
+ .clip(shape = RoundedCornerShape(18.dp)),
+ )
+
+ SpacerW12()
+
+ Column(modifier = Modifier.weight(1f)) {
+ EllipsisText(
+ text = stringResourceSafe(R.string.domain_receive_assets_onboarding_network_name, fullName),
+ ellipsis = TextEllipsis.Middle,
+ color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography.subtitle1,
+ )
+
+ EllipsisText(
+ text = address,
+ ellipsis = TextEllipsis.Middle,
+ color = TangemTheme.colors.text.tertiary,
+ style = TangemTheme.typography.caption2,
+ )
+ }
+
+ SpacerW12()
+
+ TangemIconButton(
+ modifier = Modifier.size(TangemTheme.dimens.size28),
+ iconRes = R.drawable.ic_qrcode_new_24,
+ onClick = onOpenQrCodeClick,
+ )
+
+ SpacerW8()
+
+ TangemIconButton(
+ modifier = Modifier.size(TangemTheme.dimens.size28),
+ iconRes = R.drawable.ic_copy_new_24,
+ onClick = onCopyClick,
+ )
+ }
+ }
+}
+
+@Composable
+private fun EnsItem(onCopyClick: () -> Unit, address: String, modifier: Modifier = Modifier) {
+ Card(
+ modifier = modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(containerColor = TangemTheme.colors.background.action),
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 14.dp, horizontal = 12.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Image(
+ modifier = Modifier.size(36.dp),
+ imageVector = ImageVector.vectorResource(R.drawable.ic_ens_36),
+ contentDescription = null,
+ )
+
+ SpacerW12()
+
+ EllipsisText(
+ modifier = Modifier.weight(1f),
+ text = address,
+ ellipsis = TextEllipsis.Middle,
+ color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography.subtitle1,
+ )
+
+ TangemIconButton(
+ modifier = Modifier.size(28.dp),
+ iconRes = R.drawable.ic_copy_new_24,
+ onClick = onCopyClick,
+ )
+ }
+ }
+}
+
+@Composable
+private fun LoadingBlock(modifier: Modifier = Modifier) {
+ Card(
+ modifier = modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(containerColor = TangemTheme.colors.background.action),
+ ) {
+ Box(
+ modifier = modifier
+ .fillMaxWidth()
+ .heightIn(min = 64.dp),
+ contentAlignment = Alignment.Center,
+ ) {
+ CircularProgressIndicator(
+ color = TangemTheme.colors.icon.informative,
+ modifier = Modifier.padding(TangemTheme.dimens.spacing8),
+ )
+ }
+ }
+}
+
+@Composable
+@Preview(showBackground = true, widthDp = 360)
+@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
+private fun Preview_TokenReceiveAssetsContent(
+ @PreviewParameter(TokenReceiveAssetsContentProvider::class) params: ReceiveAssetsUM,
+) {
+ TangemThemePreview {
+ TokenReceiveAssetsContent(assetsUM = params)
+ }
+}
+
+private class TokenReceiveAssetsContentProvider : PreviewParameterProvider {
+ val address = ReceiveAddress(
+ value = "0xe5178c7d4d0e861ed2e9414e045b501226b0de8d",
+ type = ReceiveAddress.Type.Default(
+ displayName = stringReference("Etherium address"),
+ ),
+ )
+ private val config = ReceiveAssetsUM(
+ notificationConfigs =
+ persistentListOf(
+ NotificationUM.Warning(
+ title = stringReference("Send only XLM on the Ethereum network"),
+ subtitle = resourceReference(R.string.receive_bottom_sheet_warning_message_description),
+ ),
+ ),
+ addresses = persistentMapOf(
+ 0 to address,
+ 1 to address.copy(type = ReceiveAddress.Type.Ens, value = "papasha.eth"),
+ ),
+ showMemoDisclaimer = false,
+ onCopyClick = {},
+ onOpenQrCodeClick = {},
+ isEnsResultLoading = true,
+ fullName = "Etherium",
+ )
+
+ override val values: Sequence
+ get() = sequenceOf(config)
+}
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt
new file mode 100644
index 0000000000..5571b27657
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt
@@ -0,0 +1,28 @@
+package com.tangem.features.tokenreceive.ui
+
+import androidx.compose.animation.animateContentSize
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.arkivanov.decompose.extensions.compose.stack.Children
+import com.arkivanov.decompose.extensions.compose.stack.animation.fade
+import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
+import com.arkivanov.decompose.router.stack.ChildStack
+import com.tangem.core.ui.decompose.ComposableContentComponent
+import com.tangem.features.tokenreceive.route.TokenReceiveRoutes
+
+@Composable
+internal fun TokenReceiveContent(
+ stackState: ChildStack,
+ modifier: Modifier = Modifier,
+) {
+ Children(
+ stack = stackState,
+ animation = stackAnimation(fade()),
+ modifier = modifier
+ .fillMaxSize()
+ .animateContentSize(),
+ ) {
+ it.instance.Content(Modifier.fillMaxSize())
+ }
+}
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt
new file mode 100644
index 0000000000..2eb6310cbd
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveQrCodeContent.kt
@@ -0,0 +1,195 @@
+package com.tangem.features.tokenreceive.ui
+
+import android.content.res.Configuration
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.graphics.painter.BitmapPainter
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.tangem.core.res.getStringSafe
+import com.tangem.core.ui.components.*
+import com.tangem.core.ui.extensions.*
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
+import com.tangem.features.tokenreceive.impl.R
+import com.tangem.features.tokenreceive.ui.state.QrCodeUM
+import kotlinx.coroutines.launch
+
+@Composable
+internal fun TokenReceiveQrCodeContent(qrCodeUM: QrCodeUM) {
+ val snackbarHostState = remember(::SnackbarHostState)
+ val qrCodePainter = rememberQrPainter(content = qrCodeUM.addressValue)
+
+ ContainerWithSnackbarHost(snackbarHostState = snackbarHostState) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(color = TangemTheme.colors.background.tertiary)
+ .padding(bottom = 16.dp)
+ .padding(horizontal = 16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ SpacerH8()
+
+ QrCodePage(
+ addressFullName = qrCodeUM.addressName,
+ addressValue = qrCodeUM.addressValue,
+ network = qrCodeUM.network,
+ qrCodePainter = qrCodePainter,
+ )
+ SpacerH24()
+
+ Buttons(
+ onShareClick = { qrCodeUM.onShareClick(qrCodeUM.addressValue) },
+ onCopyClick = { qrCodeUM.onCopyClick(qrCodeUM.addressValue) },
+ snackbarHostState = snackbarHostState,
+ )
+ }
+ }
+}
+
+@Composable
+private fun QrCodePage(addressFullName: TextReference, addressValue: String, network: String, qrCodePainter: Painter) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.size36)) {
+ Text(
+ text = stringResourceSafe(
+ R.string.receive_bottom_sheet_warning_message_compact,
+ addressFullName.resolveReference(),
+ network,
+ ),
+ color = TangemTheme.colors.text.primary1,
+ textAlign = TextAlign.Center,
+ style = TangemTheme.typography.h3,
+ )
+
+ SpacerH(20.dp)
+
+ Image(
+ painter = qrCodePainter,
+ contentDescription = null,
+ contentScale = ContentScale.Fit,
+ modifier = Modifier.size(248.dp),
+ )
+
+ SpacerH24()
+ }
+ Text(
+ text = stringResourceSafe(R.string.wc_common_address),
+ color = TangemTheme.colors.text.tertiary,
+ textAlign = TextAlign.Center,
+ style = TangemTheme.typography.subtitle2,
+ )
+
+ SpacerH2()
+
+ Text(
+ text = addressValue,
+ color = TangemTheme.colors.text.primary1,
+ textAlign = TextAlign.Center,
+ style = TangemTheme.typography.subtitle1,
+ )
+ }
+}
+
+@Composable
+private fun Buttons(
+ snackbarHostState: SnackbarHostState,
+ onShareClick: () -> Unit,
+ onCopyClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val hapticFeedback = LocalHapticFeedback.current
+ val coroutineScope = rememberCoroutineScope()
+ val context = LocalContext.current
+ val resources = context.resources
+
+ Row(
+ modifier = modifier,
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ SecondaryButtonIconStart(
+ modifier = Modifier.weight(1f),
+ text = stringResourceSafe(id = R.string.common_copy),
+ iconResId = R.drawable.ic_copy_24,
+ onClick = {
+ hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+ onCopyClick()
+ coroutineScope.launch {
+ snackbarHostState.showSnackbar(
+ message = resources.getStringSafe(R.string.wallet_notification_address_copied),
+ )
+ }
+ },
+ )
+
+ SecondaryButtonIconStart(
+ modifier = Modifier.weight(1f),
+ text = stringResourceSafe(id = R.string.common_share),
+ iconResId = R.drawable.ic_share_24,
+ onClick = {
+ hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+ onShareClick()
+ },
+ )
+ }
+}
+
+@Composable
+private fun rememberQrPainter(content: String, size: Dp = 248.dp, padding: Dp = 0.dp): BitmapPainter {
+ val density = LocalDensity.current
+ return remember(content) {
+ BitmapPainter(
+ content.toQrCode(
+ sizePx = with(density) { size.roundToPx() },
+ paddingPx = with(density) { padding.roundToPx() },
+ ).asImageBitmap(),
+ )
+ }
+}
+
+@Composable
+@Preview(showBackground = true, widthDp = 360)
+@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
+private fun Preview_TokenReceiveQrCodeContent(
+ @PreviewParameter(TokenReceiveQrCodeContentPreviewProvider::class) qrCodeUM: QrCodeUM,
+) {
+ TangemThemePreview {
+ TokenReceiveQrCodeContent(qrCodeUM = qrCodeUM)
+ }
+}
+
+private class TokenReceiveQrCodeContentPreviewProvider : PreviewParameterProvider {
+ private val config = QrCodeUM(
+ network = "Ethereum",
+ addressName = stringReference("Etherium"),
+ addressValue = "0xe5178c7d4d0e861ed2e9414e045b501226b0de8d",
+ onCopyClick = {},
+ onShareClick = {},
+ )
+
+ override val values: Sequence
+ get() = sequenceOf(config)
+}
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt
new file mode 100644
index 0000000000..2aa8aeff9c
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt
@@ -0,0 +1,142 @@
+package com.tangem.features.tokenreceive.ui
+
+import android.content.res.Configuration
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
+import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import androidx.compose.ui.unit.dp
+import com.tangem.core.ui.components.SecondaryButton
+import com.tangem.core.ui.components.SpacerH
+import com.tangem.core.ui.components.SpacerH12
+import com.tangem.core.ui.components.SpacerH24
+import com.tangem.core.ui.components.currency.icon.CurrencyIcon
+import com.tangem.core.ui.components.currency.icon.CurrencyIconState
+import com.tangem.core.ui.extensions.stringResourceSafe
+import com.tangem.core.ui.res.TangemColorPalette
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
+import com.tangem.features.tokenreceive.impl.R
+import com.tangem.features.tokenreceive.ui.state.WarningUM
+
+@Composable
+internal fun TokenReceiveWarningContent(warningUM: WarningUM) {
+ val hapticFeedback = LocalHapticFeedback.current
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(color = TangemTheme.colors.background.tertiary)
+ .padding(
+ start = 16.dp,
+ end = 16.dp,
+ bottom = 16.dp,
+ ),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ CurrencyIcon(
+ modifier = Modifier.size(size = 56.dp),
+ state = warningUM.iconState,
+ shouldDisplayNetwork = false,
+ iconSize = 56.dp,
+ )
+
+ SpacerH24()
+
+ WarningBlock(networkIcon = warningUM.networkIcon, networkName = warningUM.network)
+
+ SpacerH12()
+
+ Text(
+ textAlign = TextAlign.Center,
+ text = stringResourceSafe(R.string.domain_receive_assets_onboarding_description),
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.secondary,
+ )
+
+ SpacerH(48.dp)
+
+ SecondaryButton(
+ modifier = Modifier.fillMaxWidth(),
+ text = stringResourceSafe(R.string.common_got_it),
+ onClick = {
+ hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+ warningUM.onWarningAcknowledged()
+ },
+ )
+ }
+}
+
+@Composable
+fun WarningBlock(networkName: String, networkIcon: Int, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Text(
+ textAlign = TextAlign.Center,
+ text = stringResourceSafe(R.string.domain_receive_assets_onboarding_title),
+ style = TangemTheme.typography.h3,
+ color = TangemTheme.colors.text.primary1,
+ )
+
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Icon(
+ modifier = Modifier.size(20.dp),
+ painter = painterResource(id = networkIcon),
+ tint = Color.Unspecified,
+ contentDescription = null,
+ )
+
+ Text(
+ textAlign = TextAlign.Center,
+ text = stringResourceSafe(R.string.domain_receive_assets_onboarding_network_name, networkName),
+ style = TangemTheme.typography.h3,
+ color = TangemTheme.colors.text.primary1,
+ )
+ }
+ }
+}
+
+@Composable
+@Preview(showBackground = true, widthDp = 360)
+@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
+private fun Preview_TokenReceiveWarningContent(
+ @PreviewParameter(TokenReceiveWarningContentProvider::class) warningUM: WarningUM,
+) {
+ TangemThemePreview {
+ TokenReceiveWarningContent(warningUM = warningUM)
+ }
+}
+
+private class TokenReceiveWarningContentProvider : PreviewParameterProvider {
+ val iconState = CurrencyIconState.TokenIcon(
+ url = null,
+ topBadgeIconResId = null,
+ fallbackTint = TangemColorPalette.Black,
+ fallbackBackground = TangemColorPalette.Meadow,
+ isGrayscale = false,
+ showCustomBadge = false,
+ )
+
+ override val values: Sequence
+ get() = sequenceOf(
+ WarningUM(
+ iconState = iconState,
+ onWarningAcknowledged = {},
+ network = "Etherium",
+ networkIcon = R.drawable.ic_eth_16,
+ ),
+ )
+}
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/QrCodeUM.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/QrCodeUM.kt
new file mode 100644
index 0000000000..e3c4f5adf0
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/QrCodeUM.kt
@@ -0,0 +1,11 @@
+package com.tangem.features.tokenreceive.ui.state
+
+import com.tangem.core.ui.extensions.TextReference
+
+internal data class QrCodeUM(
+ val onCopyClick: (String) -> Unit,
+ val onShareClick: (String) -> Unit,
+ val addressName: TextReference,
+ val addressValue: String,
+ val network: String,
+)
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/ReceiveAssetsUM.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/ReceiveAssetsUM.kt
new file mode 100644
index 0000000000..3953389152
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/ReceiveAssetsUM.kt
@@ -0,0 +1,16 @@
+package com.tangem.features.tokenreceive.ui.state
+
+import com.tangem.common.ui.notifications.NotificationUM
+import com.tangem.features.tokenreceive.entity.ReceiveAddress
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.ImmutableMap
+
+internal data class ReceiveAssetsUM(
+ val showMemoDisclaimer: Boolean,
+ val addresses: ImmutableMap,
+ val onOpenQrCodeClick: (id: Int) -> Unit,
+ val onCopyClick: (id: Int) -> Unit,
+ val isEnsResultLoading: Boolean,
+ val notificationConfigs: ImmutableList,
+ val fullName: String,
+)
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/TokenReceiveUM.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/TokenReceiveUM.kt
new file mode 100644
index 0000000000..27eecde6a3
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/TokenReceiveUM.kt
@@ -0,0 +1,15 @@
+package com.tangem.features.tokenreceive.ui.state
+
+import com.tangem.common.ui.notifications.NotificationUM
+import com.tangem.core.ui.components.currency.icon.CurrencyIconState
+import com.tangem.features.tokenreceive.entity.ReceiveAddress
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.ImmutableMap
+
+internal data class TokenReceiveUM(
+ val network: String,
+ val iconState: CurrencyIconState,
+ val addresses: ImmutableMap,
+ val isEnsResultLoading: Boolean,
+ val notificationConfigs: ImmutableList,
+)
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/WarningUM.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/WarningUM.kt
new file mode 100644
index 0000000000..c31262fd58
--- /dev/null
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/WarningUM.kt
@@ -0,0 +1,10 @@
+package com.tangem.features.tokenreceive.ui.state
+
+import com.tangem.core.ui.components.currency.icon.CurrencyIconState
+
+internal data class WarningUM(
+ val network: String,
+ val networkIcon: Int,
+ val iconState: CurrencyIconState,
+ val onWarningAcknowledged: () -> Unit,
+)
\ No newline at end of file