Updated on 2026-08-14
This commit is contained in:
parent
6e67390a0d
commit
2aea20e597
12 changed files with 828 additions and 1 deletions
|
|
@ -3,6 +3,7 @@ package com.tangem.core.ui.components
|
|||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
|
|
@ -14,11 +15,15 @@ sealed interface Keyboard {
|
|||
|
||||
data class Opened(override val height: Dp) : Keyboard
|
||||
|
||||
object Closed : Keyboard {
|
||||
data object Closed : Keyboard {
|
||||
override val height: Dp = 0.dp
|
||||
}
|
||||
}
|
||||
|
||||
val Keyboard.isOpened: Boolean
|
||||
@Stable
|
||||
get() = this is Keyboard.Opened
|
||||
|
||||
/**
|
||||
* Allows to subscribe to a soft keyboard to detect when it's open/closed
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.features.managetokens.component
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
internal interface CustomTokenFormComponent {
|
||||
|
||||
fun content(scope: LazyListScope)
|
||||
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
val networkId: Network.ID,
|
||||
)
|
||||
|
||||
interface Factory {
|
||||
fun create(params: Params): CustomTokenFormComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package com.tangem.features.managetokens.component.preview
|
||||
|
||||
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.ui.AddCustomTokenBottomSheet
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
internal class PreviewAddCustomTokenComponent(
|
||||
initialState: AddCustomTokenUM = AddCustomTokenUM.NetworkSelector(),
|
||||
) : AddCustomTokenComponent {
|
||||
|
||||
private val userWalletId = UserWalletId(stringValue = "321")
|
||||
|
||||
private val previewState: MutableStateFlow<AddCustomTokenUM> = MutableStateFlow(initialState)
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet(isVisible: Boolean, onDismiss: () -> Unit) {
|
||||
val state by previewState.collectAsStateWithLifecycle()
|
||||
val config = TangemBottomSheetConfig(
|
||||
isShow = isVisible,
|
||||
onDismissRequest = onDismiss,
|
||||
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(this)
|
||||
}
|
||||
is AddCustomTokenUM.NetworkSelector -> {
|
||||
PreviewCustomTokenNetworkSelectorComponent(
|
||||
params = CustomTokenNetworkSelectorComponent.Params(
|
||||
userWalletId = userWalletId,
|
||||
selectedNetwork = s.selectedNetwork,
|
||||
onNetworkSelected = ::showForm,
|
||||
),
|
||||
networksSize = 20,
|
||||
).content(this)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun showNetworkSelector(selectedNetwork: SelectedNetworkUM) {
|
||||
previewState.update { AddCustomTokenUM.NetworkSelector(selectedNetwork) }
|
||||
}
|
||||
|
||||
private fun showForm(network: SelectedNetworkUM) {
|
||||
previewState.update {
|
||||
AddCustomTokenUM.Form(
|
||||
selectedNetwork = network,
|
||||
addTokenButton = AddCustomTokenButtonUM.Visible(
|
||||
isEnabled = false,
|
||||
onClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package com.tangem.features.managetokens.component.preview
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
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.impl.R
|
||||
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 = {},
|
||||
),
|
||||
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<CustomTokenFormUM.NotificationUM> = 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
) : CustomTokenFormComponent {
|
||||
|
||||
private val previewState = CustomTokenFormUM(
|
||||
networkName = networkName,
|
||||
contractAddress = contractAddress,
|
||||
tokenName = tokenName,
|
||||
tokenSymbol = tokenSymbol,
|
||||
tokenDecimals = tokenDecimals,
|
||||
derivationPath = derivationPath,
|
||||
notifications = notifications,
|
||||
canAddToken = canAddToken,
|
||||
onDerivationPathClick = {},
|
||||
onNetworkClick = {},
|
||||
onAddClick = {},
|
||||
)
|
||||
|
||||
override fun content(scope: LazyListScope) {
|
||||
scope.customTokenFormContent(model = previewState)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
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(
|
||||
networks = previewNetworks,
|
||||
)
|
||||
|
||||
override fun content(scope: LazyListScope) {
|
||||
scope.customTokenNetworkSelectorContent(
|
||||
model = previewState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.tangem.features.managetokens.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
||||
@Immutable
|
||||
internal sealed class AddCustomTokenUM : TangemBottomSheetConfigContent {
|
||||
|
||||
abstract val selectedNetwork: SelectedNetworkUM?
|
||||
abstract val addTokenButton: AddCustomTokenButtonUM
|
||||
|
||||
data class NetworkSelector(
|
||||
override val selectedNetwork: SelectedNetworkUM? = null,
|
||||
) : AddCustomTokenUM() {
|
||||
|
||||
override val addTokenButton: AddCustomTokenButtonUM = AddCustomTokenButtonUM.Hidden
|
||||
}
|
||||
|
||||
data class Form(
|
||||
override val selectedNetwork: SelectedNetworkUM,
|
||||
override val addTokenButton: AddCustomTokenButtonUM.Visible,
|
||||
) : AddCustomTokenUM()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
internal data class SelectedNetworkUM(
|
||||
val id: Network.ID,
|
||||
val name: String,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
internal sealed class AddCustomTokenButtonUM {
|
||||
|
||||
open val onClick: () -> Unit = {}
|
||||
|
||||
open val isEnabled: Boolean = false
|
||||
|
||||
val isVisible: Boolean
|
||||
get() = this is Visible
|
||||
|
||||
data object Hidden : AddCustomTokenButtonUM() {
|
||||
override val onClick: () -> Unit = {}
|
||||
}
|
||||
|
||||
data class Visible(
|
||||
override val isEnabled: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
) : AddCustomTokenButtonUM()
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.features.managetokens.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
internal data class CustomTokenFormUM(
|
||||
val networkName: ClickableFieldUM,
|
||||
val contractAddress: TextInputFieldUM,
|
||||
val tokenName: TextInputFieldUM,
|
||||
val tokenSymbol: TextInputFieldUM,
|
||||
val tokenDecimals: TextInputFieldUM,
|
||||
val derivationPath: ClickableFieldUM,
|
||||
val notifications: ImmutableList<NotificationUM>,
|
||||
val canAddToken: Boolean,
|
||||
val onNetworkClick: () -> Unit,
|
||||
val onDerivationPathClick: () -> Unit,
|
||||
val onAddClick: () -> Unit,
|
||||
) {
|
||||
|
||||
@Immutable
|
||||
data class NotificationUM(
|
||||
val id: String,
|
||||
val config: NotificationConfig,
|
||||
)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
internal data class TextInputFieldUM(
|
||||
val label: TextReference,
|
||||
val placeholder: TextReference,
|
||||
val value: String,
|
||||
val onValueChange: (String) -> Unit,
|
||||
val error: TextReference? = null,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
internal data class ClickableFieldUM(
|
||||
val label: TextReference,
|
||||
val value: TextReference,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.features.managetokens.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
internal data class CustomTokenNetworkSelectorUM(
|
||||
val networks: ImmutableList<CurrencyNetworkUM>,
|
||||
)
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
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.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.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
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.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
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.impl.R
|
||||
|
||||
@Composable
|
||||
internal fun AddCustomTokenBottomSheet(config: TangemBottomSheetConfig, content: LazyListScope.() -> Unit) {
|
||||
TangemBottomSheet(
|
||||
config = config,
|
||||
titleText = resourceReference(R.string.add_custom_token_title),
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
content = { model: AddCustomTokenUM ->
|
||||
Content(
|
||||
model = model,
|
||||
content = content,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@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 {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun Preview_AddCustomTokenBottomSheet(
|
||||
@PreviewParameter(AddCustomTokenComponentPreviewProvider::class) component: AddCustomTokenComponent,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
component.BottomSheet(isVisible = true, onDismiss = {})
|
||||
}
|
||||
}
|
||||
|
||||
private class AddCustomTokenComponentPreviewProvider : PreviewParameterProvider<AddCustomTokenComponent> {
|
||||
override val values: Sequence<AddCustomTokenComponent>
|
||||
get() = sequenceOf(
|
||||
PreviewAddCustomTokenComponent(),
|
||||
PreviewAddCustomTokenComponent(
|
||||
initialState = AddCustomTokenUM.Form(
|
||||
selectedNetwork = SelectedNetworkUM(
|
||||
id = Network.ID(value = "1"),
|
||||
name = "Ethereum",
|
||||
),
|
||||
addTokenButton = AddCustomTokenButtonUM.Visible(
|
||||
isEnabled = false,
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
package com.tangem.features.managetokens.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
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.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
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 com.tangem.core.ui.components.block.information.InformationBlock
|
||||
import com.tangem.core.ui.components.fields.SimpleTextField
|
||||
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
|
||||
|
||||
internal fun LazyListScope.customTokenFormContent(model: CustomTokenFormUM) {
|
||||
item {
|
||||
ClickableField(
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
model = model.networkName,
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(bottom = TangemTheme.dimens.spacing12)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
),
|
||||
) {
|
||||
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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
ClickableField(
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
model = model.derivationPath,
|
||||
)
|
||||
}
|
||||
|
||||
items(
|
||||
items = model.notifications,
|
||||
key = { it.id },
|
||||
) { notification ->
|
||||
Notification(
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
config = notification.config,
|
||||
containerColor = TangemTheme.colors.button.disabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TextField(
|
||||
model: TextInputFieldUM,
|
||||
modifier: Modifier = Modifier,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
) {
|
||||
InformationBlock(
|
||||
modifier = modifier,
|
||||
title = {
|
||||
val color by animateColorAsState(
|
||||
targetValue = if (model.error != null) {
|
||||
TangemTheme.colors.text.warning
|
||||
} else {
|
||||
TangemTheme.colors.text.tertiary
|
||||
},
|
||||
label = "Field label color",
|
||||
)
|
||||
|
||||
Text(
|
||||
text = (model.error ?: model.label).resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = color,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
SimpleTextField(
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
value = model.value,
|
||||
onValueChange = model.onValueChange,
|
||||
readOnly = false,
|
||||
placeholder = model.placeholder,
|
||||
singleLine = true,
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ClickableField(model: ClickableFieldUM, modifier: Modifier = Modifier) {
|
||||
InformationBlock(
|
||||
modifier = modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.clickable(onClick = model.onClick),
|
||||
title = {
|
||||
Text(
|
||||
text = model.label.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
text = model.value.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
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_CustomTokenFormContent(
|
||||
@PreviewParameter(PreviewCustomTokenFormComponentProvider::class)
|
||||
component: PreviewCustomTokenFormComponent,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
LazyColumn(
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
) { component.content(scope = this) }
|
||||
}
|
||||
}
|
||||
|
||||
private class PreviewCustomTokenFormComponentProvider :
|
||||
PreviewParameterProvider<PreviewCustomTokenFormComponent> {
|
||||
|
||||
override val values: Sequence<PreviewCustomTokenFormComponent>
|
||||
get() = sequenceOf(
|
||||
PreviewCustomTokenFormComponent(),
|
||||
PreviewCustomTokenFormComponent(
|
||||
contractAddress = TextInputFieldUM(
|
||||
label = stringReference("Contract address"),
|
||||
value = "0x1234567890",
|
||||
error = stringReference("Contract address is invalid"),
|
||||
placeholder = stringReference("0x1234567890"),
|
||||
onValueChange = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
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.tooling.preview.Preview
|
||||
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.features.managetokens.component.preview.PreviewCustomTokenNetworkSelectorComponent
|
||||
import com.tangem.features.managetokens.entity.CurrencyNetworkUM
|
||||
import com.tangem.features.managetokens.entity.CustomTokenNetworkSelectorUM
|
||||
import com.tangem.features.managetokens.impl.R
|
||||
|
||||
internal fun LazyListScope.customTokenNetworkSelectorContent(model: CustomTokenNetworkSelectorUM) {
|
||||
val lastIndex = model.networks.lastIndex
|
||||
|
||||
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 = "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 = if (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 = false,
|
||||
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() {
|
||||
TangemThemePreview {
|
||||
LazyColumn {
|
||||
PreviewCustomTokenNetworkSelectorComponent().content(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
Loading…
Add table
Add a link
Reference in a new issue