diff --git a/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt b/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt new file mode 100644 index 0000000000..2e3d83710f --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt @@ -0,0 +1,13 @@ +package com.tangem.core.ui.decompose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable + +@Stable +interface ComposableBottomSheetComponent { + + fun dismiss() + + @Composable + fun BottomSheet() +} \ No newline at end of file diff --git a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/AddCustomTokenComponent.kt b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/AddCustomTokenComponent.kt index 761758d465..63a0f48e26 100644 --- a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/AddCustomTokenComponent.kt +++ b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/AddCustomTokenComponent.kt @@ -1,18 +1,15 @@ package com.tangem.features.managetokens.component -import androidx.compose.runtime.Composable +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.domain.wallets.models.UserWalletId -interface AddCustomTokenComponent { - - @Composable - fun BottomSheet(isVisible: Boolean, onDismiss: () -> Unit) +interface AddCustomTokenComponent : ComposableBottomSheetComponent { data class Params( val userWalletId: UserWalletId, + val onDismiss: () -> Unit, ) - interface Factory { - fun create(params: Params): AddCustomTokenComponent - } + interface Factory : ComponentFactory } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenFormComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenFormComponent.kt index acb8dc42a4..ee90f06a95 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenFormComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenFormComponent.kt @@ -1,19 +1,20 @@ package com.tangem.features.managetokens.component -import androidx.compose.foundation.lazy.LazyListScope -import com.tangem.domain.tokens.model.Network +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath +import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork -internal interface CustomTokenFormComponent { - - fun content(scope: LazyListScope) +internal interface CustomTokenFormComponent : ComposableContentComponent { data class Params( val userWalletId: UserWalletId, - val networkId: Network.ID, + val network: SelectedNetwork, + val derivationPath: SelectedDerivationPath, + val onSelectNetworkClick: () -> Unit, + val onSelectDerivationPathClick: () -> Unit, ) - interface Factory { - fun create(params: Params): CustomTokenFormComponent - } + interface Factory : ComponentFactory } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenNetworkSelectorComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenNetworkSelectorComponent.kt deleted file mode 100644 index a2551d430c..0000000000 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenNetworkSelectorComponent.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.tangem.features.managetokens.component - -import androidx.compose.foundation.lazy.LazyListScope -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.features.managetokens.entity.SelectedNetworkUM - -internal interface CustomTokenNetworkSelectorComponent { - - fun content(scope: LazyListScope) - - data class Params( - val userWalletId: UserWalletId, - val selectedNetwork: SelectedNetworkUM?, - val onNetworkSelected: (SelectedNetworkUM) -> Unit, - ) - - interface Factory { - fun create(params: Params): CustomTokenNetworkSelectorComponent - } -} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenSelectorComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenSelectorComponent.kt new file mode 100644 index 0000000000..b3e4a159a0 --- /dev/null +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/CustomTokenSelectorComponent.kt @@ -0,0 +1,27 @@ +package com.tangem.features.managetokens.component + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath +import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork + +internal interface CustomTokenSelectorComponent : ComposableContentComponent { + + sealed class Params { + + data class NetworkSelector( + val userWalletId: UserWalletId, + val selectedNetwork: SelectedNetwork?, + val onNetworkSelected: (SelectedNetwork) -> Unit, + ) : Params() + + data class DerivationPathSelector( + val userWalletId: UserWalletId, + val selectedDerivationPath: SelectedDerivationPath?, + val onDerivationPathSelected: (SelectedDerivationPath) -> Unit, + ) : Params() + } + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewAddCustomTokenComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewAddCustomTokenComponent.kt index b428c0ed2d..a09d7b2b93 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewAddCustomTokenComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewAddCustomTokenComponent.kt @@ -4,81 +4,72 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.managetokens.component.AddCustomTokenComponent -import com.tangem.features.managetokens.component.CustomTokenNetworkSelectorComponent -import com.tangem.features.managetokens.entity.AddCustomTokenButtonUM -import com.tangem.features.managetokens.entity.AddCustomTokenUM -import com.tangem.features.managetokens.entity.ClickableFieldUM -import com.tangem.features.managetokens.entity.SelectedNetworkUM -import com.tangem.features.managetokens.impl.R +import com.tangem.features.managetokens.component.CustomTokenSelectorComponent +import com.tangem.features.managetokens.entity.customtoken.AddCustomTokenConfig import com.tangem.features.managetokens.ui.AddCustomTokenBottomSheet import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update internal class PreviewAddCustomTokenComponent( - initialState: AddCustomTokenUM = AddCustomTokenUM.NetworkSelector(popBack = {}), + initialState: AddCustomTokenConfig = AddCustomTokenConfig( + userWalletId = UserWalletId(stringValue = "321"), + step = AddCustomTokenConfig.Step.INITIAL_NETWORK_SELECTOR, + popBack = {}, + ), ) : AddCustomTokenComponent { - private val userWalletId = UserWalletId(stringValue = "321") + private val previewState: MutableStateFlow = MutableStateFlow(initialState) - private val previewState: MutableStateFlow = MutableStateFlow(initialState) + override fun dismiss() { + /* no-op */ + } @Composable - override fun BottomSheet(isVisible: Boolean, onDismiss: () -> Unit) { + override fun BottomSheet() { val state by previewState.collectAsStateWithLifecycle() val config = TangemBottomSheetConfig( - isShow = isVisible, - onDismissRequest = onDismiss, + isShow = true, + onDismissRequest = ::dismiss, content = state, ) AddCustomTokenBottomSheet( config = config, - content = { - when (val s = state) { - is AddCustomTokenUM.Form -> { - PreviewCustomTokenFormComponent( - networkName = ClickableFieldUM( - label = resourceReference(R.string.custom_token_network_input_title), - value = stringReference(s.selectedNetwork.name), - onClick = { showNetworkSelector(s.selectedNetwork) }, + content = { modifier -> + when (state.step) { + AddCustomTokenConfig.Step.INITIAL_NETWORK_SELECTOR -> { + PreviewCustomTokenSelectorComponent( + params = CustomTokenSelectorComponent.Params.NetworkSelector( + userWalletId = state.userWalletId, + selectedNetwork = null, + onNetworkSelected = {}, ), - ).content(this) + ).Content(modifier) } - is AddCustomTokenUM.NetworkSelector -> { - PreviewCustomTokenNetworkSelectorComponent( - params = CustomTokenNetworkSelectorComponent.Params( - userWalletId = userWalletId, - selectedNetwork = s.selectedNetwork, - onNetworkSelected = ::showForm, + AddCustomTokenConfig.Step.NETWORK_SELECTOR -> { + PreviewCustomTokenSelectorComponent( + params = CustomTokenSelectorComponent.Params.NetworkSelector( + userWalletId = state.userWalletId, + selectedNetwork = state.selectedNetwork, + onNetworkSelected = {}, ), - networksSize = 20, - ).content(this) + ).Content(modifier) + } + AddCustomTokenConfig.Step.DERIVATION_PATH_SELECTOR -> { + PreviewCustomTokenSelectorComponent( + params = CustomTokenSelectorComponent.Params.DerivationPathSelector( + userWalletId = state.userWalletId, + selectedDerivationPath = state.selectedDerivationPath, + onDerivationPathSelected = {}, + ), + ).Content(modifier) + } + AddCustomTokenConfig.Step.FORM -> { + PreviewCustomTokenFormComponent().Content(modifier) } } }, ) } - - private fun showNetworkSelector(selectedNetwork: SelectedNetworkUM) { - previewState.update { - AddCustomTokenUM.NetworkSelector(selectedNetwork, popBack = { showForm(selectedNetwork) }) - } - } - - private fun showForm(network: SelectedNetworkUM) { - previewState.update { - AddCustomTokenUM.Form( - popBack = {}, - selectedNetwork = network, - addTokenButton = AddCustomTokenButtonUM.Visible( - isEnabled = false, - onClick = {}, - ), - ) - } - } } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenFormComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenFormComponent.kt index 625090f28d..1c69d487de 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenFormComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenFormComponent.kt @@ -1,81 +1,87 @@ package com.tangem.features.managetokens.component.preview -import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.features.managetokens.component.CustomTokenFormComponent -import com.tangem.features.managetokens.entity.ClickableFieldUM -import com.tangem.features.managetokens.entity.CustomTokenFormUM -import com.tangem.features.managetokens.entity.TextInputFieldUM +import com.tangem.features.managetokens.entity.customtoken.ClickableFieldUM +import com.tangem.features.managetokens.entity.customtoken.CustomTokenFormUM +import com.tangem.features.managetokens.entity.customtoken.TextInputFieldUM import com.tangem.features.managetokens.impl.R -import com.tangem.features.managetokens.ui.customTokenFormContent +import com.tangem.features.managetokens.ui.CustomTokenFormContent import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf internal class PreviewCustomTokenFormComponent( - networkName: ClickableFieldUM = ClickableFieldUM( - label = resourceReference(R.string.custom_token_network_input_title), - value = stringReference(value = "Ethereum"), - onClick = {}, - ), - derivationPath: ClickableFieldUM = ClickableFieldUM( - label = resourceReference(R.string.custom_token_derivation_path), - value = stringReference(value = "Default"), - onClick = {}, - ), + networkName: ClickableFieldUM = PreviewCustomTokenFormComponent.networkName, + derivationPath: ClickableFieldUM = PreviewCustomTokenFormComponent.derivationPath, canAddToken: Boolean = false, - contractAddress: TextInputFieldUM = TextInputFieldUM( - label = resourceReference(R.string.custom_token_contract_address_input_title), - placeholder = stringReference(value = "0x000000000000000000000000000"), - value = "", - onValueChange = {}, - ), - tokenName: TextInputFieldUM = TextInputFieldUM( - label = resourceReference(R.string.custom_token_name_input_title), - placeholder = stringReference(value = "E.g. USD Coin"), - value = "", - onValueChange = {}, - ), - tokenSymbol: TextInputFieldUM = TextInputFieldUM( - label = resourceReference(R.string.custom_token_token_symbol_input_title), - placeholder = stringReference(value = "E.g. USDC"), - value = "", - onValueChange = {}, - ), - tokenDecimals: TextInputFieldUM = TextInputFieldUM( - label = resourceReference(R.string.custom_token_decimals_input_title), - placeholder = stringReference(value = "8"), - value = "", - onValueChange = {}, - ), - notifications: ImmutableList = persistentListOf( - CustomTokenFormUM.NotificationUM( - id = "1", - config = NotificationConfig( - title = stringReference(value = "Note that tokens can be created by anyone"), - subtitle = stringReference(value = "Be aware of adding scam tokens, they can cost nothing"), - iconResId = R.drawable.img_attention_20, - ), - ), - ), + tokenForm: CustomTokenFormUM.TokenFormUM? = PreviewCustomTokenFormComponent.tokenForm, + notifications: ImmutableList = PreviewCustomTokenFormComponent.notifications, ) : CustomTokenFormComponent { private val previewState = CustomTokenFormUM( networkName = networkName, - contractAddress = contractAddress, - tokenName = tokenName, - tokenSymbol = tokenSymbol, - tokenDecimals = tokenDecimals, + tokenForm = tokenForm, derivationPath = derivationPath, notifications = notifications, canAddToken = canAddToken, - onDerivationPathClick = {}, - onNetworkClick = {}, - onAddClick = {}, + saveToken = {}, ) - override fun content(scope: LazyListScope) { - scope.customTokenFormContent(model = previewState) + @Composable + override fun Content(modifier: Modifier) { + CustomTokenFormContent(modifier = modifier, model = previewState) + } + + companion object { + val networkName: ClickableFieldUM = ClickableFieldUM( + label = resourceReference(R.string.custom_token_network_input_title), + value = stringReference(value = "Ethereum"), + onClick = {}, + ) + val derivationPath: ClickableFieldUM = ClickableFieldUM( + label = resourceReference(R.string.custom_token_derivation_path), + value = stringReference(value = "Default"), + onClick = {}, + ) + val tokenForm: CustomTokenFormUM.TokenFormUM = CustomTokenFormUM.TokenFormUM( + contractAddress = TextInputFieldUM( + label = resourceReference(R.string.custom_token_contract_address_input_title), + placeholder = stringReference(value = "0x000000000000000000000000000"), + value = "", + onValueChange = {}, + ), + name = TextInputFieldUM( + label = resourceReference(R.string.custom_token_name_input_title), + placeholder = stringReference(value = "E.g. USD Coin"), + value = "", + onValueChange = {}, + ), + symbol = TextInputFieldUM( + label = resourceReference(R.string.custom_token_token_symbol_input_title), + placeholder = stringReference(value = "E.g. USDC"), + value = "", + onValueChange = {}, + ), + decimals = TextInputFieldUM( + label = resourceReference(R.string.custom_token_decimals_input_title), + placeholder = stringReference(value = "8"), + value = "", + onValueChange = {}, + ), + ) + val notifications: ImmutableList = persistentListOf( + CustomTokenFormUM.NotificationUM( + id = "1", + config = NotificationConfig( + title = stringReference(value = "Note that tokens can be created by anyone"), + subtitle = stringReference(value = "Be aware of adding scam tokens, they can cost nothing"), + iconResId = R.drawable.img_attention_20, + ), + ), + ) } } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenNetworkSelectorComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenNetworkSelectorComponent.kt deleted file mode 100644 index 620dbe1222..0000000000 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenNetworkSelectorComponent.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.tangem.features.managetokens.component.preview - -import androidx.compose.foundation.lazy.LazyListScope -import com.tangem.domain.tokens.model.Network -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.features.managetokens.component.CustomTokenNetworkSelectorComponent -import com.tangem.features.managetokens.entity.CurrencyNetworkUM -import com.tangem.features.managetokens.entity.CustomTokenNetworkSelectorUM -import com.tangem.features.managetokens.entity.SelectedNetworkUM -import com.tangem.features.managetokens.impl.R -import com.tangem.features.managetokens.ui.customTokenNetworkSelectorContent -import kotlinx.collections.immutable.toImmutableList - -internal class PreviewCustomTokenNetworkSelectorComponent( - private val params: CustomTokenNetworkSelectorComponent.Params = CustomTokenNetworkSelectorComponent.Params( - userWalletId = UserWalletId(stringValue = "321"), - selectedNetwork = null, - onNetworkSelected = {}, - ), - networksSize: Int = 5, -) : CustomTokenNetworkSelectorComponent { - - private val previewNetworks = List(size = networksSize) { networkIndex -> - val n = SelectedNetworkUM( - id = Network.ID(networkIndex.toString()), - name = "Network $networkIndex", - ) - - CurrencyNetworkUM( - id = n.id, - name = n.name, - type = "N$networkIndex", - iconResId = R.drawable.ic_eth_16, - isMainNetwork = false, - isSelected = n.id == params.selectedNetwork?.id, - onSelectedStateChange = { params.onNetworkSelected(n) }, - ) - }.toImmutableList() - - private val previewState = CustomTokenNetworkSelectorUM( - showTitle = params.selectedNetwork == null, - networks = previewNetworks, - ) - - override fun content(scope: LazyListScope) { - scope.customTokenNetworkSelectorContent( - model = previewState, - ) - } -} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt new file mode 100644 index 0000000000..1e773cc319 --- /dev/null +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt @@ -0,0 +1,79 @@ +package com.tangem.features.managetokens.component.preview + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.tangem.core.ui.extensions.stringReference +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.features.managetokens.component.CustomTokenSelectorComponent +import com.tangem.features.managetokens.component.CustomTokenSelectorComponent.Params +import com.tangem.features.managetokens.entity.customtoken.CustomTokenSelectorUM +import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath +import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork +import com.tangem.features.managetokens.entity.item.CurrencyNetworkUM +import com.tangem.features.managetokens.entity.item.DerivationPathUM +import com.tangem.features.managetokens.impl.R +import com.tangem.features.managetokens.ui.CustomTokenSelectorContent +import kotlinx.collections.immutable.toImmutableList + +internal class PreviewCustomTokenSelectorComponent( + private val params: Params = Params.NetworkSelector( + userWalletId = UserWalletId(stringValue = "321"), + selectedNetwork = null, + onNetworkSelected = {}, + ), + itemsSize: Int = 5, +) : CustomTokenSelectorComponent { + + private val previewItems = List(size = itemsSize) { index -> + when (params) { + is Params.DerivationPathSelector -> { + val d = SelectedDerivationPath( + value = "m/44'/0'/0'/0/$index", + name = stringReference(value = "Network $index"), + ) + + DerivationPathUM( + value = "m/44'/0'/0'/0/$index", + blockchainName = d.name, + isSelected = d.value == params.selectedDerivationPath?.value, + onSelectedStateChange = { params.onDerivationPathSelected(d) }, + ) + } + is Params.NetworkSelector -> { + val n = SelectedNetwork( + id = Network.ID(index.toString()), + name = stringReference(value = "Network $index"), + derivationPath = "m/44'/0'/0'/0/$index", + ) + + CurrencyNetworkUM( + networkId = n.id, + name = "Network $index", + type = "N$index", + iconResId = R.drawable.ic_eth_16, + isMainNetwork = false, + isSelected = n.id == params.selectedNetwork?.id, + onSelectedStateChange = { params.onNetworkSelected(n) }, + ) + } + } + }.toImmutableList() + + val previewState = CustomTokenSelectorUM( + header = when (params) { + is Params.DerivationPathSelector -> CustomTokenSelectorUM.HeaderUM.CustomDerivationButton({}) + is Params.NetworkSelector -> if (params.selectedNetwork == null) { + CustomTokenSelectorUM.HeaderUM.Description + } else { + CustomTokenSelectorUM.HeaderUM.None + } + }, + items = previewItems, + ) + + @Composable + override fun Content(modifier: Modifier) { + CustomTokenSelectorContent(modifier = modifier, model = previewState) + } +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt index 89103d7f03..efa89d5b8e 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt @@ -13,10 +13,10 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.managetokens.model.ManagedCryptoCurrency import com.tangem.domain.tokens.model.Network import com.tangem.features.managetokens.component.ManageTokensComponent -import com.tangem.features.managetokens.entity.CurrencyItemUM -import com.tangem.features.managetokens.entity.CurrencyNetworkUM -import com.tangem.features.managetokens.entity.ManageTokensTopBarUM -import com.tangem.features.managetokens.entity.ManageTokensUM +import com.tangem.features.managetokens.entity.item.CurrencyItemUM +import com.tangem.features.managetokens.entity.item.CurrencyNetworkUM +import com.tangem.features.managetokens.entity.managetokens.ManageTokensTopBarUM +import com.tangem.features.managetokens.entity.managetokens.ManageTokensUM import com.tangem.features.managetokens.impl.R import com.tangem.features.managetokens.ui.ManageTokensScreen import kotlinx.collections.immutable.mutate @@ -134,7 +134,7 @@ internal class PreviewManageTokensComponent : ManageTokensComponent { private fun getCurrencyNetworks(currencyIndex: Int) = List(size = 3) { networkIndex -> CurrencyNetworkUM( - id = Network.ID(networkIndex.toString()), + networkId = Network.ID(networkIndex.toString()), name = "NETWORK$networkIndex", type = "N$networkIndex", iconResId = R.drawable.ic_eth_16, diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt index c348a45e46..557b3fc508 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt @@ -13,7 +13,6 @@ import com.tangem.core.ui.message.SnackbarMessage import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.managetokens.component.ManageTokensComponent import com.tangem.features.managetokens.entity.item.CurrencyItemUM -import com.tangem.features.managetokens.entity.managetokens.BottomSheetConfig import com.tangem.features.managetokens.entity.managetokens.ManageTokensTopBarUM import com.tangem.features.managetokens.entity.managetokens.ManageTokensUM import com.tangem.features.managetokens.impl.R diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/AddCustomTokenBottomSheet.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/AddCustomTokenBottomSheet.kt index a002b54b4c..b1b3cc0f0a 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/AddCustomTokenBottomSheet.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/AddCustomTokenBottomSheet.kt @@ -1,146 +1,80 @@ package com.tangem.features.managetokens.ui import android.content.res.Configuration -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.material3.FabPosition -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.onSizeChanged -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.stringResource -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.PrimaryButton import com.tangem.core.ui.components.appbar.TangemTopAppBar import com.tangem.core.ui.components.appbar.TangemTopAppBarHeight import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetTitle -import com.tangem.core.ui.components.isOpened -import com.tangem.core.ui.components.keyboardAsState import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.domain.tokens.model.Network +import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.managetokens.component.AddCustomTokenComponent import com.tangem.features.managetokens.component.preview.PreviewAddCustomTokenComponent -import com.tangem.features.managetokens.entity.AddCustomTokenButtonUM -import com.tangem.features.managetokens.entity.AddCustomTokenUM -import com.tangem.features.managetokens.entity.SelectedNetworkUM +import com.tangem.features.managetokens.entity.customtoken.AddCustomTokenConfig +import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath +import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork import com.tangem.features.managetokens.impl.R @Composable -internal fun AddCustomTokenBottomSheet(config: TangemBottomSheetConfig, content: LazyListScope.() -> Unit) { - TangemBottomSheet( +internal fun AddCustomTokenBottomSheet(config: TangemBottomSheetConfig, content: @Composable (Modifier) -> Unit) { + TangemBottomSheet( config = config, + addBottomInsets = false, title = { model -> Title(model) }, containerColor = TangemTheme.colors.background.secondary, - content = { model -> - Content( - model = model, - content = content, - ) + content = { + val contentModifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing16) + .fillMaxSize() + + content(contentModifier) }, ) } @Composable -private fun Title(model: AddCustomTokenUM, modifier: Modifier = Modifier) { - val showTokenNetworkTitle = model is AddCustomTokenUM.NetworkSelector && model.selectedNetwork != null - - if (showTokenNetworkTitle) { - TangemTopAppBar( - modifier = modifier, - title = resourceReference(R.string.custom_token_network_selector_title), - titleAlignment = Alignment.CenterHorizontally, - startButton = TopAppBarButtonUM.Back(model.popBack), - height = TangemTopAppBarHeight.BOTTOM_SHEET, - ) - } else { - TangemBottomSheetTitle( - modifier = modifier, - title = resourceReference(R.string.add_custom_token_title), - ) - } -} - -@Composable -private fun Content(model: AddCustomTokenUM, content: LazyListScope.() -> Unit, modifier: Modifier = Modifier) { - val density = LocalDensity.current - val keyboardState by keyboardAsState() - - var fabHeight by remember { mutableStateOf(0.dp) } - - Scaffold( - modifier = modifier.imePadding(), - containerColor = TangemTheme.colors.background.secondary, - floatingActionButtonPosition = FabPosition.Center, - floatingActionButton = { - AnimatedVisibility( - modifier = Modifier.onSizeChanged { - fabHeight = with(density) { it.height.toDp() } - }, - visible = model.addTokenButton.isVisible && !keyboardState.isOpened, - enter = fadeIn(), - exit = fadeOut(), - label = "Add button visibility", - ) { - PrimaryButton( - modifier = Modifier - .padding(bottom = TangemTheme.dimens.spacing16) - .padding(horizontal = TangemTheme.dimens.spacing16) - .fillMaxWidth(), - text = stringResource(id = R.string.custom_token_add_token), - enabled = model.addTokenButton.isEnabled, - onClick = model.addTokenButton.onClick, - ) - } - }, - ) { paddingValues -> - LazyColumn( - modifier = Modifier.padding(paddingValues), - contentPadding = PaddingValues( - start = TangemTheme.dimens.spacing16, - end = TangemTheme.dimens.spacing16, - bottom = TangemTheme.dimens.spacing32 + fabHeight, - ), - ) { - item { - if (model is AddCustomTokenUM.NetworkSelector && model.selectedNetwork != null) { - Spacer(modifier = Modifier.size(TangemTheme.dimens.spacing12)) - } else { - Box( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = TangemTheme.dimens.spacing16), - contentAlignment = Alignment.Center, - ) { - Text( - modifier = Modifier.fillMaxWidth(fraction = 0.7f), - text = stringResource(id = R.string.custom_token_subtitle), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.secondary, - textAlign = TextAlign.Center, - ) - } - } - } - - content() +private fun Title(model: AddCustomTokenConfig, modifier: Modifier = Modifier) { + when (model.step) { + AddCustomTokenConfig.Step.INITIAL_NETWORK_SELECTOR, + AddCustomTokenConfig.Step.FORM, + -> { + TangemBottomSheetTitle( + modifier = modifier, + title = resourceReference(R.string.add_custom_token_title), + ) + } + AddCustomTokenConfig.Step.NETWORK_SELECTOR -> { + TangemTopAppBar( + modifier = modifier, + title = resourceReference(R.string.custom_token_network_selector_title), + titleAlignment = Alignment.CenterHorizontally, + startButton = TopAppBarButtonUM.Back(model.popBack), + height = TangemTopAppBarHeight.BOTTOM_SHEET, + ) + } + AddCustomTokenConfig.Step.DERIVATION_PATH_SELECTOR -> { + TangemTopAppBar( + modifier = modifier, + title = resourceReference(R.string.custom_token_derivation_path), + titleAlignment = Alignment.CenterHorizontally, + startButton = TopAppBarButtonUM.Back(model.popBack), + height = TangemTopAppBarHeight.BOTTOM_SHEET, + ) } } } @@ -153,7 +87,7 @@ private fun Preview_AddCustomTokenBottomSheet( @PreviewParameter(AddCustomTokenComponentPreviewProvider::class) component: AddCustomTokenComponent, ) { TangemThemePreview { - component.BottomSheet(isVisible = true, onDismiss = {}) + component.BottomSheet() } } @@ -162,24 +96,37 @@ private class AddCustomTokenComponentPreviewProvider : PreviewParameterProvider< get() = sequenceOf( PreviewAddCustomTokenComponent(), PreviewAddCustomTokenComponent( - initialState = AddCustomTokenUM.NetworkSelector( + initialState = AddCustomTokenConfig( + userWalletId = UserWalletId(stringValue = "321"), + step = AddCustomTokenConfig.Step.FORM, popBack = {}, - selectedNetwork = SelectedNetworkUM( - id = Network.ID(value = "0"), - name = "Ethereum", + selectedNetwork = SelectedNetwork( + id = Network.ID(value = "1"), + name = stringReference("Ethereum"), + derivationPath = "m/44'/0'/0'/0/0", ), ), ), PreviewAddCustomTokenComponent( - initialState = AddCustomTokenUM.Form( + initialState = AddCustomTokenConfig( + userWalletId = UserWalletId(stringValue = "321"), + step = AddCustomTokenConfig.Step.NETWORK_SELECTOR, popBack = {}, - selectedNetwork = SelectedNetworkUM( - id = Network.ID(value = "1"), - name = "Ethereum", + selectedNetwork = SelectedNetwork( + id = Network.ID(value = "0"), + name = stringReference("Ethereum"), + derivationPath = "m/44'/0'/0'/0/0", ), - addTokenButton = AddCustomTokenButtonUM.Visible( - isEnabled = false, - onClick = {}, + ), + ), + PreviewAddCustomTokenComponent( + initialState = AddCustomTokenConfig( + userWalletId = UserWalletId(stringValue = "321"), + step = AddCustomTokenConfig.Step.DERIVATION_PATH_SELECTOR, + popBack = {}, + selectedDerivationPath = SelectedDerivationPath( + value = "m/44'/0'/0'/0/0", + name = stringReference("Ethereum"), ), ), ), diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenFormContent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenFormContent.kt index e898e44150..76ae892e76 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenFormContent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenFormContent.kt @@ -2,97 +2,152 @@ package com.tangem.features.managetokens.ui import android.content.res.Configuration import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text 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.platform.LocalDensity +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType 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.util.fastForEach +import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.block.information.InformationBlock import com.tangem.core.ui.components.fields.SimpleTextField +import com.tangem.core.ui.components.isOpened +import com.tangem.core.ui.components.keyboardAsState import com.tangem.core.ui.components.notifications.Notification 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.managetokens.component.preview.PreviewCustomTokenFormComponent -import com.tangem.features.managetokens.entity.ClickableFieldUM -import com.tangem.features.managetokens.entity.CustomTokenFormUM -import com.tangem.features.managetokens.entity.TextInputFieldUM +import com.tangem.features.managetokens.entity.customtoken.ClickableFieldUM +import com.tangem.features.managetokens.entity.customtoken.CustomTokenFormUM +import com.tangem.features.managetokens.entity.customtoken.TextInputFieldUM +import com.tangem.features.managetokens.impl.R +import com.tangem.features.managetokens.ui.component.AddCustomTokenDescription -internal fun LazyListScope.customTokenFormContent(model: CustomTokenFormUM) { - item { - ClickableField( - modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12), - model = model.networkName, - ) - } +@Composable +internal fun CustomTokenFormContent(model: CustomTokenFormUM, modifier: Modifier = Modifier) { + val keyboard by keyboardAsState() + val bottomBarHeight by animateDpAsState( + label = "Bottom bar height", + targetValue = if (keyboard.isOpened) { + TangemTheme.dimens.spacing0 + } else { + with(LocalDensity.current) { + WindowInsets.systemBars.getBottom(density = this).toDp() + } + }, + ) + + Box( + modifier = modifier + .imePadding() + .fillMaxSize() + .background(color = TangemTheme.colors.background.secondary), + ) { + val scrollState = rememberScrollState() - item { Column( modifier = Modifier - .padding(bottom = TangemTheme.dimens.spacing12) - .background( - color = TangemTheme.colors.background.action, - shape = TangemTheme.shapes.roundedCornersXMedium, - ), + .verticalScroll(scrollState) + .fillMaxSize() + .padding(bottom = TangemTheme.dimens.spacing76), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), ) { - TextField( - model = model.contractAddress, - keyboardOptions = KeyboardOptions.Default.copy( - imeAction = ImeAction.Next, - ), - ) - TextField( - model = model.tokenName, - keyboardOptions = KeyboardOptions.Default.copy( - imeAction = ImeAction.Next, - ), - ) - TextField( - model = model.tokenSymbol, - keyboardOptions = KeyboardOptions.Default.copy( - imeAction = ImeAction.Next, - ), - ) - TextField( - model = model.tokenDecimals, - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Decimal, - imeAction = ImeAction.Next, - ), + AddCustomTokenDescription() + FormContent(model) + } + + PrimaryButton( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = TangemTheme.dimens.spacing16 + bottomBarHeight) + .fillMaxWidth(), + text = stringResource(id = R.string.custom_token_add_token), + enabled = model.canAddToken, + onClick = model.saveToken, + ) + } +} + +@Composable +private fun FormContent(model: CustomTokenFormUM, modifier: Modifier = Modifier) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), + ) { + ClickableField( + model = model.networkName, + ) + + val tokenForm = model.tokenForm + if (tokenForm != null) { + TokenForm(tokenForm) + } + + ClickableField( + model = model.derivationPath, + ) + + model.notifications.fastForEach { notification -> + Notification( + config = notification.config, + containerColor = TangemTheme.colors.button.disabled, ) } } +} - item { - ClickableField( - modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12), - model = model.derivationPath, +@Composable +private fun TokenForm(tokenForm: CustomTokenFormUM.TokenFormUM, modifier: Modifier = Modifier) { + Column( + modifier = modifier + .background( + color = TangemTheme.colors.background.action, + shape = TangemTheme.shapes.roundedCornersXMedium, + ), + ) { + TextField( + model = tokenForm.contractAddress, + keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Next, + ), ) - } - - items( - items = model.notifications, - key = { it.id }, - ) { notification -> - Notification( - modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12), - config = notification.config, - containerColor = TangemTheme.colors.button.disabled, + TextField( + model = tokenForm.name, + keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Next, + ), + ) + TextField( + model = tokenForm.symbol, + keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Next, + ), + ) + TextField( + model = tokenForm.decimals, + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Decimal, + imeAction = ImeAction.Next, + ), ) } } @@ -124,7 +179,9 @@ private fun TextField( }, content = { SimpleTextField( - modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12), + modifier = Modifier + .padding(bottom = TangemTheme.dimens.spacing12) + .fillMaxWidth(), value = model.value, onValueChange = model.onValueChange, readOnly = false, @@ -170,9 +227,7 @@ private fun Preview_CustomTokenFormContent( component: PreviewCustomTokenFormComponent, ) { TangemThemePreview { - LazyColumn( - modifier = Modifier.background(color = TangemTheme.colors.background.secondary), - ) { component.content(scope = this) } + component.Content(modifier = Modifier) } } @@ -183,14 +238,19 @@ private class PreviewCustomTokenFormComponentProvider : get() = sequenceOf( PreviewCustomTokenFormComponent(), PreviewCustomTokenFormComponent( - contractAddress = TextInputFieldUM( - label = stringReference("Contract address"), - value = "0x1234567890", - error = stringReference("Contract address is invalid"), - placeholder = stringReference("0x1234567890"), - onValueChange = {}, + tokenForm = PreviewCustomTokenFormComponent.tokenForm.copy( + contractAddress = TextInputFieldUM( + label = stringReference("Contract address"), + value = "0x1234567890", + error = stringReference("Contract address is invalid"), + placeholder = stringReference("0x1234567890"), + onValueChange = {}, + ), ), ), + PreviewCustomTokenFormComponent( + tokenForm = null, + ), ) } // endregion Preview \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenNetworkSelectorContent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenNetworkSelectorContent.kt deleted file mode 100644 index 2363fff9c9..0000000000 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenNetworkSelectorContent.kt +++ /dev/null @@ -1,158 +0,0 @@ -package com.tangem.features.managetokens.ui - -import android.content.res.Configuration -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -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.res.TangemTheme -import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.domain.tokens.model.Network -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.features.managetokens.component.CustomTokenNetworkSelectorComponent -import com.tangem.features.managetokens.component.preview.PreviewCustomTokenNetworkSelectorComponent -import com.tangem.features.managetokens.entity.CurrencyNetworkUM -import com.tangem.features.managetokens.entity.CustomTokenNetworkSelectorUM -import com.tangem.features.managetokens.entity.SelectedNetworkUM -import com.tangem.features.managetokens.impl.R - -internal fun LazyListScope.customTokenNetworkSelectorContent(model: CustomTokenNetworkSelectorUM) { - val lastIndex = model.networks.lastIndex - - if (model.showTitle) { - item { - Box( - modifier = Modifier - .fillMaxWidth() - .heightIn(min = TangemTheme.dimens.size36) - .background( - color = TangemTheme.colors.background.primary, - shape = TangemTheme.shapes.bottomSheet, - ), - ) { - Text( - modifier = Modifier - .padding( - top = TangemTheme.dimens.spacing12, - bottom = TangemTheme.dimens.spacing6, - ) - .padding(horizontal = TangemTheme.dimens.spacing12), - text = stringResource(R.string.add_custom_token_choose_network), - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.tertiary, - ) - } - } - } - - itemsIndexed( - items = model.networks, - key = { _, item -> item.id.value }, - ) { index, item -> - NetworkItem( - modifier = Modifier - .fillMaxWidth() - .clip( - shape = when { - !model.showTitle && index == 0 -> RoundedCornerShape( - topStart = TangemTheme.dimens.radius16, - topEnd = TangemTheme.dimens.radius16, - ) - index == lastIndex -> RoundedCornerShape( - bottomStart = TangemTheme.dimens.radius16, - bottomEnd = TangemTheme.dimens.radius16, - ) - else -> RectangleShape - }, - ) - .background(color = TangemTheme.colors.background.primary) - .clickable(onClick = { item.onSelectedStateChange(true) }) - .padding(horizontal = TangemTheme.dimens.spacing4), - model = item, - ) - } -} - -@Composable -private fun NetworkItem(model: CurrencyNetworkUM, modifier: Modifier = Modifier) { - ChainRow( - modifier = modifier, - model = with(model) { - ChainRowUM( - name = name, - type = type, - icon = CurrencyIconState.CoinIcon( - url = null, - fallbackResId = model.iconResId, - isGrayscale = !model.isSelected, - showCustomBadge = false, - ), - showCustom = false, - ) - }, - action = { - AnimatedVisibility( - modifier = Modifier.size(TangemTheme.dimens.size24), - visible = model.isSelected, - ) { - Icon( - painter = painterResource(id = R.drawable.ic_check_24), - tint = TangemTheme.colors.icon.accent, - contentDescription = null, - ) - } - }, - ) -} - -// region Preview -@Composable -@Preview(showBackground = true, widthDp = 360) -@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) -private fun Preview_CustomTokenNetworkSelectorContent( - @PreviewParameter(CustomTokenNetworkSelectorComponentPreviewProvider::class) - component: CustomTokenNetworkSelectorComponent, -) { - TangemThemePreview { - LazyColumn { - component.content(this) - } - } -} - -private class CustomTokenNetworkSelectorComponentPreviewProvider : - PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf( - PreviewCustomTokenNetworkSelectorComponent(), - PreviewCustomTokenNetworkSelectorComponent( - params = CustomTokenNetworkSelectorComponent.Params( - userWalletId = UserWalletId(stringValue = "321"), - selectedNetwork = SelectedNetworkUM( - id = Network.ID(value = "0"), - name = "", - ), - onNetworkSelected = {}, - ), - ), - ) -} -// endregion Preview \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenSelectorContent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenSelectorContent.kt new file mode 100644 index 0000000000..f5545db44b --- /dev/null +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/CustomTokenSelectorContent.kt @@ -0,0 +1,255 @@ +package com.tangem.features.managetokens.ui + +import android.content.res.Configuration +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.RoundedCornerShape +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.draw.clip +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +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.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.features.managetokens.component.CustomTokenSelectorComponent +import com.tangem.features.managetokens.component.preview.PreviewCustomTokenSelectorComponent +import com.tangem.features.managetokens.entity.customtoken.CustomTokenSelectorUM +import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath +import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork +import com.tangem.features.managetokens.entity.item.CurrencyNetworkUM +import com.tangem.features.managetokens.entity.item.DerivationPathUM +import com.tangem.features.managetokens.impl.R +import com.tangem.features.managetokens.ui.component.AddCustomTokenDescription + +@Composable +internal fun CustomTokenSelectorContent(model: CustomTokenSelectorUM, modifier: Modifier = Modifier) { + val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() } + val lastIndex = model.items.lastIndex + + LazyColumn( + modifier = modifier, + contentPadding = PaddingValues( + bottom = TangemTheme.dimens.spacing16 + bottomBarHeight, + ), + ) { + item { + Header(model.header) + } + + itemsIndexed( + items = model.items, + key = { _, item -> item.id }, + ) { index, item -> + val itemModifier = Modifier + .fillMaxWidth() + .clip( + shape = when { + model.header !is CustomTokenSelectorUM.HeaderUM.Description && index == 0 -> { + RoundedCornerShape( + topStart = TangemTheme.dimens.radius16, + topEnd = TangemTheme.dimens.radius16, + ) + } + index == lastIndex -> { + RoundedCornerShape( + bottomStart = TangemTheme.dimens.radius16, + bottomEnd = TangemTheme.dimens.radius16, + ) + } + else -> { + RectangleShape + } + }, + ) + .background(color = TangemTheme.colors.background.primary) + .clickable(onClick = { item.onSelectedStateChange(true) }) + .padding(horizontal = TangemTheme.dimens.spacing4) + + when (item) { + is CurrencyNetworkUM -> { + NetworkItem( + modifier = itemModifier, + model = item, + ) + } + is DerivationPathUM -> { + DerivationPathItem( + modifier = itemModifier, + model = item, + ) + } + } + } + } +} + +@Composable +private fun Header(header: CustomTokenSelectorUM.HeaderUM, modifier: Modifier = Modifier) { + when (header) { + is CustomTokenSelectorUM.HeaderUM.CustomDerivationButton -> { + CustomDerivationButton( + modifier = modifier.padding(vertical = TangemTheme.dimens.spacing16), + onClick = header.onClick, + ) + } + is CustomTokenSelectorUM.HeaderUM.Description -> { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AddCustomTokenDescription() + Box( + modifier = modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size36) + .background( + color = TangemTheme.colors.background.primary, + shape = TangemTheme.shapes.bottomSheet, + ), + ) { + Text( + modifier = Modifier + .padding(top = TangemTheme.dimens.spacing12) + .padding(horizontal = TangemTheme.dimens.spacing12), + text = stringResource(R.string.add_custom_token_choose_network), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + } + } + } + is CustomTokenSelectorUM.HeaderUM.None -> { + Spacer(modifier = Modifier.size(TangemTheme.dimens.spacing16)) + } + } +} + +@Composable +private fun NetworkItem(model: CurrencyNetworkUM, modifier: Modifier = Modifier) { + ChainRow( + modifier = modifier, + model = with(model) { + ChainRowUM( + name = name, + type = type, + icon = CurrencyIconState.CoinIcon( + url = null, + fallbackResId = model.iconResId, + isGrayscale = !model.isSelected, + showCustomBadge = false, + ), + showCustom = false, + ) + }, + action = { + AnimatedVisibility( + modifier = Modifier.size(TangemTheme.dimens.size24), + visible = model.isSelected, + ) { + Icon( + painter = painterResource(id = R.drawable.ic_check_24), + tint = TangemTheme.colors.icon.accent, + contentDescription = null, + ) + } + }, + ) +} + +@Composable +private fun CustomDerivationButton(onClick: () -> Unit, modifier: Modifier = Modifier) { + // TODO: Implement in [REDACTED_JIRA] + Box( + modifier = modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size56) + .clip(shape = TangemTheme.shapes.roundedCornersXMedium) + .background(color = TangemTheme.colors.background.primary) + .clickable(onClick = onClick) + .padding(all = TangemTheme.dimens.spacing12), + contentAlignment = Alignment.CenterStart, + ) { + Text( + text = "Custom", + color = TangemTheme.colors.text.primary1, + ) + } +} + +@Composable +private fun DerivationPathItem(model: DerivationPathUM, modifier: Modifier = Modifier) { + // TODO: Implement in [REDACTED_JIRA] + Box( + modifier = modifier + .heightIn(TangemTheme.dimens.size56) + .padding(all = TangemTheme.dimens.spacing12), + contentAlignment = Alignment.CenterStart, + ) { + Text( + text = "${model.value} ${if (model.isSelected) "- selected" else ""}", + color = TangemTheme.colors.text.primary1, + ) + } +} + +// region Preview +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun Preview_CustomTokenNetworkSelectorContent( + @PreviewParameter(CustomTokenNetworkSelectorComponentPreviewProvider::class) + component: CustomTokenSelectorComponent, +) { + TangemThemePreview { + component.Content(modifier = Modifier) + } +} + +private class CustomTokenNetworkSelectorComponentPreviewProvider : + PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + PreviewCustomTokenSelectorComponent(), + PreviewCustomTokenSelectorComponent( + params = CustomTokenSelectorComponent.Params.NetworkSelector( + userWalletId = UserWalletId(stringValue = "321"), + selectedNetwork = SelectedNetwork( + id = Network.ID(value = "0"), + name = stringReference(""), + derivationPath = "m/44'/0'/0'/0/0", + ), + onNetworkSelected = {}, + ), + ), + PreviewCustomTokenSelectorComponent( + params = CustomTokenSelectorComponent.Params.DerivationPathSelector( + userWalletId = UserWalletId(stringValue = "321"), + selectedDerivationPath = SelectedDerivationPath( + value = "m/44'/0'/0'/0/0", + name = stringReference(""), + ), + onDerivationPathSelected = {}, + ), + ), + ) +} +// endregion Preview \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/component/AddCustomTokenDescription.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/component/AddCustomTokenDescription.kt new file mode 100644 index 0000000000..3b95cd9e8c --- /dev/null +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/component/AddCustomTokenDescription.kt @@ -0,0 +1,21 @@ +package com.tangem.features.managetokens.ui.component + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.managetokens.impl.R + +@Composable +internal fun AddCustomTokenDescription(modifier: Modifier = Modifier) { + Text( + modifier = modifier.fillMaxWidth(fraction = 0.7f), + text = stringResource(id = R.string.custom_token_subtitle), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.secondary, + textAlign = TextAlign.Center, + ) +} \ No newline at end of file