diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 190b4f48ad..530ffd016c 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -265,7 +265,6 @@
Рыночная капитализация
Выбранный токен не доступен в кошельке на данный момент. Но не переживайте, вы можете выразить свой интерес проголосовав за его добавление.
Голосовать
- Кошелек не поддерживает выбранную монету
Выберите кошелек
Кошелёк не поддерживает более одной сети
Вам необходимо установить единый код доступа для защиты всех ваших карт
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 4aea85ef4c..414edfc00e 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -286,7 +286,6 @@
Coin market cap
The selected token is currently unavailable for actions within the crypto wallet. But worry not, you can express your interest by upvoting it.
Upvote
- Wallet Incompatible with selected Coin
Choose wallet
The wallet doesn\'t support more than one network
You have to set up a single access code to protect all your wallets
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt
index a9eddb52f7..1e2301844b 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt
@@ -5,13 +5,13 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material.ButtonColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.components.buttons.common.*
-import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
import com.tangem.core.ui.res.TangemTheme
// region TextButton
@@ -19,7 +19,13 @@ import com.tangem.core.ui.res.TangemTheme
* [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=97%3A103&t=TmfD6UBHPg9uYfev-4)
* */
@Composable
-fun TextButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true) {
+fun TextButton(
+ text: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ colors: ButtonColors = TangemButtonsDefaults.defaultTextButtonColors,
+ enabled: Boolean = true,
+) {
TangemButton(
modifier = modifier,
text = text,
@@ -27,7 +33,7 @@ fun TextButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier,
onClick = onClick,
enabled = enabled,
showProgress = false,
- colors = TangemButtonsDefaults.defaultTextButtonColors,
+ colors = colors,
size = TangemButtonSize.Text,
)
}
@@ -41,6 +47,7 @@ fun TextButtonIconStart(
@DrawableRes iconResId: Int,
onClick: () -> Unit,
modifier: Modifier = Modifier,
+ colors: ButtonColors = TangemButtonsDefaults.defaultTextButtonColors,
enabled: Boolean = true,
) {
TangemButton(
@@ -50,7 +57,7 @@ fun TextButtonIconStart(
onClick = onClick,
enabled = enabled,
showProgress = false,
- colors = TangemButtonsDefaults.defaultTextButtonColors,
+ colors = colors,
size = TangemButtonSize.Text,
)
}
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt
index 41e2d008b6..f332e61cbf 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt
@@ -7,9 +7,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.material.RadioButton
-import androidx.compose.material.RadioButtonDefaults
-import androidx.compose.material.Text
+import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.remember
@@ -24,6 +22,8 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SelctorDialogParamsProvider.SelectorDialogParams
+import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
+import com.tangem.core.ui.components.fields.SimpleDialogTextField
import com.tangem.core.ui.res.TangemTheme
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@@ -118,6 +118,34 @@ fun TextInputDialog(
)
}
+@Composable
+fun TextInputDialog(
+ fieldValue: String,
+ confirmButton: DialogButton,
+ onDismissDialog: () -> Unit,
+ onValueChange: (String) -> Unit,
+ textFieldParams: AdditionalTextInputDialogParams,
+ title: String? = null,
+ dismissButton: DialogButton? = null,
+ isDismissable: Boolean = true,
+) {
+ TangemDialog(
+ type = DialogType.SimpleTextInput(
+ value = fieldValue,
+ onValueChange = onValueChange,
+ params = textFieldParams,
+ ),
+ confirmButton = confirmButton,
+ onDismissDialog = onDismissDialog,
+ title = title,
+ dismissButton = dismissButton,
+ properties = DialogProperties(
+ dismissOnBackPress = isDismissable,
+ dismissOnClickOutside = isDismissable,
+ ),
+ )
+}
+
@Composable
fun SelectorDialog(
selectedItemIndex: Int,
@@ -164,6 +192,7 @@ data class AdditionalTextInputDialogParams(
val caption: String? = null,
val enabled: Boolean = true,
val isError: Boolean = false,
+ val errorText: String? = null,
)
// region Defaults
@@ -194,6 +223,7 @@ private fun TangemDialog(
style = when (type) {
is DialogType.Message -> TangemTheme.typography.h2
is DialogType.TextInput -> TangemTheme.typography.h3
+ is DialogType.SimpleTextInput -> TangemTheme.typography.h3
is DialogType.Selector -> TangemTheme.typography.h2
},
color = TangemTheme.colors.text.primary1,
@@ -231,6 +261,16 @@ private fun DialogContent(type: DialogType, modifier: Modifier = Modifier) {
color = TangemTheme.colors.text.secondary,
)
}
+ is DialogType.SimpleTextInput -> {
+ SimpleDialogTextField(
+ value = type.value,
+ onValueChange = type.onValueChange,
+ isError = type.params.isError,
+ errorText = type.params.errorText,
+ isEnabled = type.params.enabled,
+ placeholder = type.params.placeholder,
+ )
+ }
is DialogType.TextInput -> {
OutlineTextField(
modifier = Modifier
@@ -305,6 +345,7 @@ private fun DialogButton(
text = text,
enabled = enabled,
onClick = onClick,
+ colors = TangemButtonsDefaults.positiveButtonColors,
)
}
}
@@ -371,6 +412,12 @@ private sealed class DialogType {
val params: AdditionalTextInputDialogParams = AdditionalTextInputDialogParams(),
) : DialogType()
+ data class SimpleTextInput(
+ val value: String,
+ val onValueChange: (String) -> Unit,
+ val params: AdditionalTextInputDialogParams = AdditionalTextInputDialogParams(),
+ ) : DialogType()
+
data class Selector(
val selectedItemIndex: Int,
val items: ImmutableList,
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonsDefaults.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonsDefaults.kt
index 1511a25c2f..db255b710f 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonsDefaults.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonsDefaults.kt
@@ -66,4 +66,14 @@ internal object TangemButtonsDefaults {
disabledBackgroundColor = Color.Transparent,
disabledContentColor = TangemTheme.colors.text.disabled,
)
+
+ val positiveButtonColors: ButtonColors
+ @Composable
+ @ReadOnlyComposable
+ get() = TangemButtonColors(
+ backgroundColor = Color.Transparent,
+ contentColor = TangemTheme.colors.text.accent,
+ disabledBackgroundColor = Color.Transparent,
+ disabledContentColor = TangemTheme.colors.text.disabled,
+ )
}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleDialogTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleDialogTextField.kt
new file mode 100644
index 0000000000..098f576a94
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleDialogTextField.kt
@@ -0,0 +1,97 @@
+package com.tangem.core.ui.components.fields
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.SolidColor
+import com.tangem.core.ui.res.TangemTheme
+
+/**
+ * A simple input field for a basic dialog with input
+ *
+ * [Show in Figma] (https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=48%3A30&mode=design
+ * &t=OsU3Nqsm9d8WDNuv-1)
+ */
+@Composable
+fun SimpleDialogTextField(
+ value: String,
+ onValueChange: (String) -> Unit,
+ isError: Boolean = false,
+ errorText: String? = null,
+ isEnabled: Boolean = true,
+ placeholder: String? = null,
+) {
+ val strokeColor = if (isError) {
+ TangemTheme.colors.icon.warning
+ } else {
+ TangemTheme.colors.stroke.primary
+ }
+ Column {
+ BasicTextField(
+ value = value,
+ onValueChange = onValueChange,
+ textStyle = TangemTheme.typography.body1,
+ cursorBrush = SolidColor(TangemTheme.colors.text.primary1),
+ singleLine = true,
+ readOnly = !isEnabled,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = TangemTheme.dimens.spacing24)
+ .drawBehind {
+ val strokeWidth = 2f
+ val y = size.height - strokeWidth / 2
+ drawLine(
+ strokeColor,
+ Offset(0f, y),
+ Offset(size.width, y),
+ strokeWidth,
+ )
+ },
+ decorationBox = { textValue ->
+ Box {
+ this@Column.AnimatedVisibility(
+ visible = value.isBlank() && placeholder != null,
+ enter = fadeIn(),
+ exit = fadeOut(),
+ ) {
+ if (value.isBlank() && placeholder != null) {
+ Text(
+ text = placeholder,
+ style = TangemTheme.typography.body1,
+ color = TangemTheme.colors.text.disabled,
+ )
+ }
+ }
+ textValue()
+ }
+ },
+ )
+ AnimatedVisibility(
+ visible = errorText != null,
+ enter = fadeIn(),
+ exit = fadeOut(),
+ ) {
+ if (errorText != null) {
+ Text(
+ text = errorText,
+ style = TangemTheme.typography.caption2,
+ color = TangemTheme.colors.text.warning,
+ modifier = Modifier
+ .padding(
+ horizontal = TangemTheme.dimens.spacing24,
+ ),
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/ui/ChooseWalletScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/ui/ChooseWalletScreen.kt
index 617fed74bd..878a5f28e1 100644
--- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/ui/ChooseWalletScreen.kt
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/ui/ChooseWalletScreen.kt
@@ -45,7 +45,9 @@ internal fun ChooseWalletScreen(state: ChooseWalletState.Choose, modifier: Modif
) {
IconButton(
onClick = state.onCloseChoosingWalletClick,
- modifier = Modifier.align(Alignment.CenterStart),
+ modifier = Modifier
+ .align(Alignment.CenterStart)
+ .clickable { state.onCloseChoosingWalletClick() },
) {
Icon(
painterResource(id = R.drawable.ic_back_24),
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/AddCustomTokenPreviewData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/AddCustomTokenPreviewData.kt
new file mode 100644
index 0000000000..998c00a3e7
--- /dev/null
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/AddCustomTokenPreviewData.kt
@@ -0,0 +1,49 @@
+package com.tangem.managetokens.presentation.customtokens.state.previewdata
+
+import com.tangem.managetokens.presentation.common.state.ChooseWalletState
+import com.tangem.managetokens.presentation.common.state.WalletState
+import com.tangem.managetokens.presentation.customtokens.state.*
+import kotlinx.collections.immutable.persistentListOf
+import kotlinx.collections.immutable.persistentSetOf
+
+internal object AddCustomTokenPreviewData {
+ val state = AddCustomTokenState(
+ chooseWalletState = ChooseWalletState.Choose(
+ wallets = persistentListOf(),
+ selectedWallet = WalletState(
+ "",
+ "",
+ "My Wallet",
+ {},
+ ),
+ onChooseWalletClick = { },
+ onCloseChoosingWalletClick = { },
+ ),
+ chooseNetworkState = ChooseNetworkState(
+ networks = persistentListOf(),
+ selectedNetwork = null,
+ onChooseNetworkClick = { },
+ onCloseChoosingNetworkClick = {},
+ ),
+ chooseDerivationState = ChooseDerivationState(
+ derivations = persistentListOf(),
+ selectedDerivation = null,
+ enterCustomDerivationState = null,
+ onChooseDerivationClick = { },
+ onCloseChoosingDerivationClick = {},
+ onEnterCustomDerivation = {},
+ ),
+ tokenData = CustomTokenData(
+ contractAddressTextField = TextFieldState.Editable(
+ value = "0x4ace7262705b68bcba5b91de96889349394",
+ isEnabled = false,
+ onValueChange = {},
+ ),
+ nameTextField = TextFieldState.Loading,
+ symbolTextField = TextFieldState.Loading,
+ decimalsTextField = TextFieldState.Loading,
+ ),
+ warnings = persistentSetOf(),
+ addTokenButton = ButtonState(isEnabled = true, onClick = {}),
+ )
+}
\ No newline at end of file
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseDerivationPreviewData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseDerivationPreviewData.kt
new file mode 100644
index 0000000000..2bb35400d2
--- /dev/null
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseDerivationPreviewData.kt
@@ -0,0 +1,42 @@
+package com.tangem.managetokens.presentation.customtokens.state.previewdata
+
+import com.tangem.managetokens.presentation.customtokens.state.ChooseDerivationState
+import com.tangem.managetokens.presentation.customtokens.state.Derivation
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.persistentListOf
+
+internal object ChooseDerivationPreviewData {
+ private val derivations: ImmutableList = persistentListOf(
+ Derivation(
+ networkName = "Ethereum",
+ path = "m/44’/9001’/0’/0/0",
+ networkId = "ethereum",
+ standardType = "ERC",
+ onDerivationSelected = {},
+ ),
+ Derivation(
+ networkName = "Polygon",
+ path = "m/44’/9001’/0’/0/0",
+ networkId = "polygon",
+ standardType = "ERC",
+ onDerivationSelected = {},
+ ),
+ Derivation(
+ networkName = "Avalanche",
+ path = "m/44’/9001’/0’/0/0",
+ networkId = "avalanche",
+ standardType = "ERC",
+ onDerivationSelected = {},
+ ),
+ )
+
+ val state = ChooseDerivationState(
+ derivations = derivations,
+ selectedDerivation = derivations.first(),
+ enterCustomDerivationState = null,
+ onEnterCustomDerivation = {},
+ onCloseChoosingDerivationClick = {},
+ onChooseDerivationClick = {},
+ show = true,
+ )
+}
\ No newline at end of file
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseNetworkCustomPreviewData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseNetworkCustomPreviewData.kt
new file mode 100644
index 0000000000..5618ce7fb4
--- /dev/null
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseNetworkCustomPreviewData.kt
@@ -0,0 +1,32 @@
+package com.tangem.managetokens.presentation.customtokens.state.previewdata
+
+import com.tangem.features.managetokens.impl.R
+import com.tangem.managetokens.presentation.common.state.NetworkItemState
+import com.tangem.managetokens.presentation.customtokens.state.ChooseNetworkState
+import kotlinx.collections.immutable.persistentListOf
+
+internal object ChooseNetworkCustomPreviewData {
+ val networks = persistentListOf(
+ NetworkItemState.Selectable(
+ name = "Ethereum",
+ protocolName = "ETH",
+ iconResId = R.drawable.img_kusama_22,
+ id = "ethereum",
+ onNetworkClick = { },
+ ),
+ NetworkItemState.Selectable(
+ name = "BNB SMART CHAIN",
+ protocolName = "BEP20",
+ iconResId = R.drawable.ic_bsc_16,
+ id = "binance smart chain",
+ onNetworkClick = { },
+ ),
+ )
+ val state = ChooseNetworkState(
+ networks,
+ selectedNetwork = networks.first(),
+ onCloseChoosingNetworkClick = {},
+ onChooseNetworkClick = {},
+ show = true,
+ )
+}
\ No newline at end of file
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseDerivationScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseDerivationScreen.kt
new file mode 100644
index 0000000000..625ff06edd
--- /dev/null
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseDerivationScreen.kt
@@ -0,0 +1,114 @@
+package com.tangem.managetokens.presentation.customtokens.ui
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.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.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.tangem.core.ui.decorations.roundedShapeItemDecoration
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.features.managetokens.impl.R
+import com.tangem.managetokens.presentation.common.ui.components.SimpleSelectionBlock
+import com.tangem.managetokens.presentation.customtokens.state.ChooseDerivationState
+import com.tangem.managetokens.presentation.customtokens.state.previewdata.ChooseDerivationPreviewData
+
+@Composable
+internal fun ChooseDerivationScreen(state: ChooseDerivationState, modifier: Modifier = Modifier) {
+ if (state.enterCustomDerivationState != null) {
+ CustomDerivationDialog(state.enterCustomDerivationState)
+ }
+
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .background(color = TangemTheme.colors.background.tertiary)
+ .statusBarsPadding()
+ .navigationBarsPadding()
+ .padding(
+ top = TangemTheme.dimens.spacing10,
+ start = TangemTheme.dimens.spacing16,
+ end = TangemTheme.dimens.spacing16,
+ bottom = TangemTheme.dimens.spacing18,
+ ),
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .defaultMinSize(minHeight = TangemTheme.dimens.size44)
+ .padding(bottom = TangemTheme.dimens.spacing12),
+ contentAlignment = Alignment.CenterStart,
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_back_24),
+ contentDescription = null,
+ tint = TangemTheme.colors.icon.primary1,
+ modifier = Modifier
+ .clickable { state.onCloseChoosingDerivationClick() },
+ )
+ Text(
+ text = stringResource(id = R.string.custom_token_derivation_path),
+ color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography.subtitle1,
+ modifier = Modifier
+ .padding(horizontal = TangemTheme.dimens.spacing24)
+ .align(Alignment.Center),
+ )
+ }
+ DerivationsList(state)
+ }
+}
+
+@Composable
+private fun DerivationsList(state: ChooseDerivationState) {
+ LazyColumn(
+ modifier = Modifier
+ .padding(horizontal = TangemTheme.dimens.spacing16),
+ contentPadding = PaddingValues(vertical = TangemTheme.dimens.spacing12),
+ ) {
+ item {
+ SimpleSelectionBlock(
+ title = stringResource(id = R.string.custom_token_custom_derivation),
+ subtitle = stringResource(id = R.string.custom_token_custom_derivation),
+ onClick = state.onEnterCustomDerivation,
+ modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
+ )
+ }
+ items(state.derivations.count()) { index ->
+ val derivationItem = state.derivations[index]
+ SimpleSelectionBlock(
+ title = derivationItem.networkName,
+ subtitle = derivationItem.path,
+ onClick = { derivationItem.onDerivationSelected(derivationItem) },
+ modifier = Modifier.roundedShapeItemDecoration(
+ currentIndex = index,
+ lastIndex = state.derivations.lastIndex,
+ addDefaultPadding = false,
+ ),
+ roundedCorners = false,
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun Preview_ChooseDerivationScreen_Light() {
+ TangemTheme(isDark = false) {
+ ChooseDerivationScreen(state = ChooseDerivationPreviewData.state)
+ }
+}
+
+@Preview
+@Composable
+private fun Preview_ChooseDerivationScreen_Dark() {
+ TangemTheme(isDark = true) {
+ ChooseDerivationScreen(state = ChooseDerivationPreviewData.state)
+ }
+}
\ No newline at end of file
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseNetworkCustomScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseNetworkCustomScreen.kt
new file mode 100644
index 0000000000..c18fac8a6b
--- /dev/null
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseNetworkCustomScreen.kt
@@ -0,0 +1,94 @@
+package com.tangem.managetokens.presentation.customtokens.ui
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.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.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.tangem.core.ui.decorations.roundedShapeItemDecoration
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.features.managetokens.impl.R
+import com.tangem.managetokens.presentation.common.ui.components.NetworkItem
+import com.tangem.managetokens.presentation.customtokens.state.ChooseNetworkState
+import com.tangem.managetokens.presentation.customtokens.state.previewdata.ChooseNetworkCustomPreviewData
+
+@Composable
+internal fun ChooseNetworkCustomScreen(state: ChooseNetworkState, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .background(color = TangemTheme.colors.background.tertiary)
+ .statusBarsPadding()
+ .navigationBarsPadding()
+ .padding(
+ top = TangemTheme.dimens.spacing10,
+ start = TangemTheme.dimens.spacing16,
+ end = TangemTheme.dimens.spacing16,
+ bottom = TangemTheme.dimens.spacing18,
+ ),
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .defaultMinSize(minHeight = TangemTheme.dimens.size44)
+ .padding(bottom = TangemTheme.dimens.spacing12),
+ contentAlignment = Alignment.CenterStart,
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_back_24),
+ contentDescription = null,
+ tint = TangemTheme.colors.icon.primary1,
+ modifier = Modifier
+ .clickable { state.onCloseChoosingNetworkClick() },
+ )
+ Text(
+ text = stringResource(id = R.string.custom_token_network_selector_title),
+ color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography.subtitle1,
+ modifier = Modifier
+ .padding(horizontal = TangemTheme.dimens.spacing24)
+ .align(Alignment.Center),
+ )
+ }
+ LazyColumn(
+ contentPadding = PaddingValues(vertical = TangemTheme.dimens.spacing12),
+ ) {
+ items(state.networks.count()) { index ->
+ NetworkItem(
+ state = state.networks[index],
+ tokenState = null,
+ isSelected = state.selectedNetwork == state.networks[index],
+ modifier = Modifier
+ .roundedShapeItemDecoration(
+ currentIndex = index,
+ lastIndex = state.networks.lastIndex,
+ addDefaultPadding = false,
+ ),
+ )
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun Preview_ChooseNetworkScreen_Light() {
+ TangemTheme(isDark = false) {
+ ChooseNetworkCustomScreen(ChooseNetworkCustomPreviewData.state)
+ }
+}
+
+@Preview
+@Composable
+private fun Preview_ChooseNetworkScreen_Dark() {
+ TangemTheme(isDark = true) {
+ ChooseNetworkCustomScreen(ChooseNetworkCustomPreviewData.state)
+ }
+}
\ No newline at end of file
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomDerivationDialog.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomDerivationDialog.kt
new file mode 100644
index 0000000000..db9a76365f
--- /dev/null
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomDerivationDialog.kt
@@ -0,0 +1,43 @@
+package com.tangem.managetokens.presentation.customtokens.ui
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import com.tangem.core.ui.components.AdditionalTextInputDialogParams
+import com.tangem.core.ui.components.DialogButton
+import com.tangem.core.ui.components.TextInputDialog
+import com.tangem.features.managetokens.impl.R
+import com.tangem.managetokens.presentation.customtokens.state.EnterCustomDerivationState
+
+@Composable
+internal fun CustomDerivationDialog(state: EnterCustomDerivationState) {
+ val confirmButton = DialogButton(
+ title = stringResource(id = R.string.common_ok),
+ enabled = state.confirmButtonEnabled,
+ onClick = state.onConfirmButtonClick,
+ )
+
+ val dismissButton = DialogButton(
+ title = stringResource(id = R.string.common_cancel),
+ onClick = state.onDismiss,
+ )
+
+ val params = AdditionalTextInputDialogParams(
+ placeholder = stringResource(id = R.string.custom_token_custom_derivation_placeholder),
+ isError = state.derivationIncorrect,
+ errorText = if (state.derivationIncorrect) {
+ stringResource(R.string.custom_token_invalid_derivation_path)
+ } else {
+ null
+ },
+ )
+
+ TextInputDialog(
+ fieldValue = state.value,
+ confirmButton = confirmButton,
+ onDismissDialog = state.onDismiss,
+ onValueChange = state.onValueChange,
+ textFieldParams = params,
+ title = stringResource(id = R.string.custom_token_custom_derivation_title),
+ dismissButton = dismissButton,
+ )
+}
\ No newline at end of file
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensChooseWalletScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensChooseWalletScreen.kt
new file mode 100644
index 0000000000..358d2d6841
--- /dev/null
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensChooseWalletScreen.kt
@@ -0,0 +1,20 @@
+package com.tangem.managetokens.presentation.customtokens.ui
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.tangem.managetokens.presentation.common.state.ChooseWalletState
+import com.tangem.managetokens.presentation.common.ui.ChooseWalletScreen
+
+@Composable
+internal fun CustomTokensChooseWalletScreen(state: ChooseWalletState.Choose) {
+ ChooseWalletScreen(
+ state = state,
+ modifier = Modifier
+ .fillMaxSize()
+ .statusBarsPadding()
+ .navigationBarsPadding(),
+ )
+}
\ No newline at end of file
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensScreen.kt
new file mode 100644
index 0000000000..98f97905a6
--- /dev/null
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensScreen.kt
@@ -0,0 +1,256 @@
+package com.tangem.managetokens.presentation.customtokens.ui
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import com.tangem.core.ui.components.PrimaryButton
+import com.tangem.core.ui.components.RectangleShimmer
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.extensions.resolveReference
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.features.managetokens.impl.R
+import com.tangem.managetokens.presentation.common.state.AlertState
+import com.tangem.managetokens.presentation.common.state.ChooseWalletState
+import com.tangem.managetokens.presentation.common.ui.ChooseWalletBottomSheet
+import com.tangem.managetokens.presentation.common.ui.ChooseWalletBottomSheetConfig
+import com.tangem.managetokens.presentation.common.ui.EventEffect
+import com.tangem.managetokens.presentation.common.ui.components.Alert
+import com.tangem.managetokens.presentation.common.ui.components.SimpleSelectionBlock
+import com.tangem.managetokens.presentation.customtokens.state.AddCustomTokenState
+import com.tangem.managetokens.presentation.customtokens.state.CustomTokenData
+import com.tangem.managetokens.presentation.customtokens.state.TextFieldState
+import com.tangem.managetokens.presentation.customtokens.state.previewdata.AddCustomTokenPreviewData
+
+@Composable
+internal fun CustomTokensScreen(state: AddCustomTokenState, modifier: Modifier = Modifier) {
+ var alertState by remember { mutableStateOf(value = null) }
+
+ EventEffect(
+ event = state.event,
+ onAlertStateSet = { alertState = it },
+ )
+ alertState?.let {
+ Alert(state = it, onDismiss = { alertState = null })
+ }
+ Content(state = state, modifier = modifier)
+}
+
+@Composable
+private fun Content(state: AddCustomTokenState, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier
+ .background(color = TangemTheme.colors.background.tertiary)
+ .statusBarsPadding()
+ .navigationBarsPadding()
+ .padding(
+ top = TangemTheme.dimens.spacing10,
+ start = TangemTheme.dimens.spacing16,
+ end = TangemTheme.dimens.spacing16,
+ bottom = TangemTheme.dimens.spacing18,
+ )
+ .fillMaxWidth(),
+ ) {
+ Text(
+ text = stringResource(id = R.string.manage_tokens_network_selector_title),
+ color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography.subtitle1,
+ textAlign = TextAlign.Center,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(bottom = TangemTheme.dimens.spacing10),
+ )
+ Text(
+ text = stringResource(id = R.string.custom_token_subtitle),
+ color = TangemTheme.colors.text.secondary,
+ style = TangemTheme.typography.caption2,
+ textAlign = TextAlign.Center,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(bottom = TangemTheme.dimens.spacing16),
+ )
+ CustomTokenItemsList(state = state)
+ PrimaryButton(
+ text = stringResource(id = R.string.custom_token_add_token),
+ onClick = state.addTokenButton.onClick,
+ enabled = state.addTokenButton.isEnabled,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+
+ if (state.chooseWalletState is ChooseWalletState.Choose && state.chooseWalletState.show) {
+ val config = TangemBottomSheetConfig(
+ isShow = true,
+ content = ChooseWalletBottomSheetConfig(state.chooseWalletState),
+ onDismissRequest = state.chooseWalletState.onCloseChoosingWalletClick,
+ )
+ ChooseWalletBottomSheet(config)
+ }
+}
+
+@Composable
+private fun ColumnScope.CustomTokenItemsList(state: AddCustomTokenState, modifier: Modifier = Modifier) {
+ LazyColumn(
+ modifier
+ .fillMaxWidth()
+ .weight(1f),
+ ) {
+ if (state.chooseWalletState is ChooseWalletState.Choose) {
+ item {
+ SimpleSelectionBlock(
+ title = stringResource(id = R.string.manage_tokens_network_selector_wallet),
+ subtitle = state.chooseWalletState.selectedWallet?.walletName ?: "",
+ onClick = state.chooseWalletState.onChooseWalletClick,
+ modifier = Modifier
+ .padding(bottom = TangemTheme.dimens.spacing12),
+ )
+ }
+ }
+ item {
+ SimpleSelectionBlock(
+ title = stringResource(id = R.string.custom_token_network_input_title),
+ subtitle = state.chooseNetworkState.selectedNetwork?.name
+ ?: stringResource(id = R.string.manage_tokens_network_selector_title),
+ onClick = state.chooseNetworkState.onChooseNetworkClick,
+ modifier = Modifier
+ .padding(bottom = TangemTheme.dimens.spacing12),
+ )
+ }
+ if (state.tokenData != null) {
+ item {
+ TokenFields(
+ state = state.tokenData,
+ modifier = Modifier
+ .padding(bottom = TangemTheme.dimens.spacing12),
+ )
+ }
+ }
+ if (state.chooseDerivationState != null) {
+ item {
+ SimpleSelectionBlock(
+ title = stringResource(id = R.string.custom_token_derivation_path),
+ subtitle = state.chooseDerivationState.selectedDerivation?.path
+ ?: stringResource(id = R.string.custom_token_derivation_path_default),
+ onClick = state.chooseDerivationState.onChooseDerivationClick,
+ modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun TokenFields(state: CustomTokenData, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier
+ .fillMaxWidth()
+ .clip(shape = RoundedCornerShape(TangemTheme.dimens.radius16))
+ .background(color = TangemTheme.colors.background.action),
+ ) {
+ TokenField(
+ textFieldState = state.contractAddressTextField,
+ placeholder = "0x0000000000000000000000000000000",
+ title = R.string.custom_token_contract_address_input_title,
+ )
+ TokenField(
+ textFieldState = state.nameTextField,
+ placeholder = stringResource(id = R.string.custom_token_name_input_placeholder),
+ title = R.string.custom_token_name_input_title,
+ )
+ TokenField(
+ textFieldState = state.symbolTextField,
+ placeholder = stringResource(id = R.string.custom_token_token_symbol_input_placeholder),
+ title = R.string.custom_token_token_symbol_input_title,
+ )
+ TokenField(
+ textFieldState = state.decimalsTextField,
+ placeholder = "0",
+ title = R.string.custom_token_decimals_input_title,
+ )
+ }
+}
+
+@Composable
+private fun TokenField(
+ textFieldState: TextFieldState,
+ placeholder: String,
+ title: Int,
+ keyboardType: KeyboardType = KeyboardType.Text,
+) {
+ Column(
+ modifier = Modifier
+ .background(color = TangemTheme.colors.background.action)
+ .padding(TangemTheme.dimens.spacing16),
+ ) {
+ TokenTextFieldTitle(
+ state = textFieldState,
+ title = stringResource(id = title),
+ )
+ when (textFieldState) {
+ is TextFieldState.Editable -> TokenTextField(
+ state = textFieldState,
+ placeholder = placeholder,
+ keyboardType = keyboardType,
+ )
+ is TextFieldState.Loading -> TokenShimmer()
+ }
+ }
+}
+
+@Composable
+private fun TokenShimmer() {
+ RectangleShimmer(
+ radius = TangemTheme.dimens.radius3,
+ modifier = Modifier
+ .padding(vertical = TangemTheme.dimens.spacing4)
+ .size(
+ height = TangemTheme.dimens.size12,
+ width = TangemTheme.dimens.size90,
+ ),
+ )
+}
+
+@Composable
+private fun TokenTextFieldTitle(state: TextFieldState?, title: String) {
+ val modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing4)
+ val error = (state as? TextFieldState.Editable)?.error
+ if (error == null) {
+ Text(
+ text = title,
+ color = TangemTheme.colors.text.secondary,
+ style = TangemTheme.typography.subtitle1,
+ modifier = modifier,
+ )
+ } else {
+ Text(
+ text = error.title.resolveReference(),
+ color = TangemTheme.colors.text.warning,
+ style = TangemTheme.typography.subtitle1,
+ modifier = modifier,
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun Preview_ChooseDerivationScreen_Light() {
+ TangemTheme(isDark = false) {
+ CustomTokensScreen(state = AddCustomTokenPreviewData.state)
+ }
+}
+
+@Preview
+@Composable
+private fun Preview_ChooseDerivationScreen_Dark() {
+ TangemTheme(isDark = true) {
+ CustomTokensScreen(state = AddCustomTokenPreviewData.state)
+ }
+}
\ No newline at end of file
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/TokenTextField.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/TokenTextField.kt
new file mode 100644
index 0000000000..a615d22738
--- /dev/null
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/TokenTextField.kt
@@ -0,0 +1,54 @@
+package com.tangem.managetokens.presentation.customtokens.ui
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.managetokens.presentation.customtokens.state.TextFieldState
+
+@Composable
+internal fun TokenTextField(
+ state: TextFieldState.Editable,
+ placeholder: String,
+ keyboardType: KeyboardType = KeyboardType.Text,
+) {
+ BasicTextField(
+ value = state.value,
+ onValueChange = state.onValueChange,
+ keyboardOptions = KeyboardOptions(keyboardType = keyboardType, imeAction = ImeAction.Default),
+ singleLine = true,
+ maxLines = 1,
+ textStyle = TangemTheme.typography.subtitle1.copy(
+ fontWeight = FontWeight.Normal,
+ color = if (state.isEnabled) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.disabled,
+ ),
+ cursorBrush = SolidColor(TangemTheme.colors.icon.primary1),
+ modifier = Modifier
+ .fillMaxWidth(),
+ decorationBox = { innerTextField ->
+ Row(modifier = Modifier.fillMaxWidth()) {
+ if (state.value.isEmpty()) {
+ Text(
+ text = placeholder,
+ color = if (state.isEnabled) {
+ TangemTheme.colors.text.tertiary
+ } else {
+ TangemTheme.colors.text.disabled
+ },
+ style = TangemTheme.typography.body2,
+ )
+ }
+ }
+ innerTextField()
+ },
+ enabled = state.isEnabled,
+ )
+}
\ No newline at end of file
diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt
index feeab80123..dca7527d4b 100644
--- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt
+++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt
@@ -14,6 +14,11 @@ import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.navigation.ReduxNavController
import com.tangem.managetokens.ManageTokensFragment
+import com.tangem.managetokens.presentation.common.state.ChooseWalletState
+import com.tangem.managetokens.presentation.customtokens.ui.ChooseDerivationScreen
+import com.tangem.managetokens.presentation.customtokens.ui.ChooseNetworkCustomScreen
+import com.tangem.managetokens.presentation.customtokens.ui.CustomTokensChooseWalletScreen
+import com.tangem.managetokens.presentation.customtokens.ui.CustomTokensScreen
import com.tangem.managetokens.presentation.customtokens.viewmodels.CustomTokensViewModel
import com.tangem.managetokens.presentation.managetokens.ui.ManageTokensScreen
import com.tangem.managetokens.presentation.managetokens.viewmodels.ManageTokensViewModel
@@ -50,8 +55,7 @@ internal class DefaultManageTokensRouter(
val viewModel = hiltViewModel(viewModelStoreOwner).apply {
router = this@DefaultManageTokensRouter
}
- // CustomTokensScreen(state = viewModel.uiState)
- // TODO: enable in [REDACTED_JIRA]
+ CustomTokensScreen(state = viewModel.uiState)
}
composable(
ManageTokensRoute.CustomTokens.ChooseNetwork.route,
@@ -59,10 +63,9 @@ internal class DefaultManageTokensRouter(
val viewModel = hiltViewModel(viewModelStoreOwner).apply {
router = this@DefaultManageTokensRouter
}
- // ChooseNetworkCustomScreen(
- // state = viewModel.uiState.chooseNetworkState,
- // )
- // TODO: enable in [REDACTED_JIRA]
+ ChooseNetworkCustomScreen(
+ state = viewModel.uiState.chooseNetworkState,
+ )
}
composable(
ManageTokensRoute.CustomTokens.ChooseDerivation.route,
@@ -70,10 +73,9 @@ internal class DefaultManageTokensRouter(
val viewModel = hiltViewModel(viewModelStoreOwner).apply {
router = this@DefaultManageTokensRouter
}
- // ChooseDerivationScreen(
- // state = requireNotNull(viewModel.uiState.chooseDerivationState),
- // )
- // TODO: enable in [REDACTED_JIRA]
+ ChooseDerivationScreen(
+ state = requireNotNull(viewModel.uiState.chooseDerivationState),
+ )
}
composable(
ManageTokensRoute.CustomTokens.ChooseWallet.route,
@@ -81,10 +83,9 @@ internal class DefaultManageTokensRouter(
val viewModel = hiltViewModel(viewModelStoreOwner).apply {
router = this@DefaultManageTokensRouter
}
- // CustomTokensChooseWalletScreen(
- // state = viewModel.uiState.chooseWalletState as ChooseWalletState.Choose,
- // )
- // TODO: enable in [REDACTED_JIRA]
+ CustomTokensChooseWalletScreen(
+ state = viewModel.uiState.chooseWalletState as ChooseWalletState.Choose,
+ )
}
}
}