Updated on 2026-08-14
This commit is contained in:
commit
b166701147
15 changed files with 344 additions and 198 deletions
|
|
@ -18,6 +18,7 @@ object Compose {
|
|||
const val material = "androidx.compose.material:material:" + Versions.compose
|
||||
const val ui = "androidx.compose.ui:ui:" + Versions.compose
|
||||
const val uiTooling = "androidx.compose.ui:ui-tooling:" + Versions.compose
|
||||
const val constraintLayout = "androidx.constraintlayout:constraintlayout-compose:" + Versions.composeConstraint
|
||||
}
|
||||
|
||||
object Firebase {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ object Versions {
|
|||
// region Compose
|
||||
const val compose = "1.3.1"
|
||||
const val composeFoundation = "1.3.1"
|
||||
const val composeConstraint = "1.0.1"
|
||||
// endregion Compose
|
||||
|
||||
// region Other libraries
|
||||
|
|
@ -60,7 +61,7 @@ object Versions {
|
|||
// endregion Other libraries
|
||||
|
||||
// region Tangem
|
||||
const val tangemBlockchainSdk = "develop-151"
|
||||
const val tangemBlockchainSdk = "develop-154"
|
||||
// const val tangemBlockchainSdk = "0.0.1" // Keep it! - used for local builds
|
||||
|
||||
const val tangemCardSdk = "develop-180"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
package com.tangem.core.ui.components.appbar
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
|
|
@ -26,6 +29,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextRange
|
||||
|
|
@ -40,13 +44,15 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
/**
|
||||
* App bar with close icon and search functionality
|
||||
*
|
||||
* @param title optional title
|
||||
* @param title optional title
|
||||
* @param placeholderSearchText optional text placeholder for search TextField
|
||||
* @param expandedInitially whether the search is expanded on launch
|
||||
* @param tint tint for most of the visual elements of toolbar
|
||||
* @param onBackClick action when close button is clicked
|
||||
* @param onSearchChange action when search is modified
|
||||
* @param onSearchDisplayClose action when search is closed
|
||||
* @param subtitle additional text under the title (optional)
|
||||
* @param icon additional icon under the title (optional)
|
||||
*
|
||||
* @see <a href =
|
||||
* "https://www.figma.com/file/Vs6SkVsFnUPsSCNwlnVf5U/Android-%E2%80%93-UI?node-id=1123%3A4068&t=xj8BBj5DfCWn2Mli-1"
|
||||
|
|
@ -61,6 +67,8 @@ fun ExpandableSearchView(
|
|||
placeholderSearchText: String = "",
|
||||
expandedInitially: Boolean = false,
|
||||
tint: Color = TangemTheme.colors.icon.primary1,
|
||||
subtitle: String? = null,
|
||||
icon: Painter? = null,
|
||||
) {
|
||||
val (expanded, onExpandedChanged) = remember { mutableStateOf(expandedInitially) }
|
||||
|
||||
|
|
@ -79,6 +87,8 @@ fun ExpandableSearchView(
|
|||
onBackClick = onBackClick,
|
||||
onExpandedChange = onExpandedChanged,
|
||||
tint = tint,
|
||||
subtitle = subtitle,
|
||||
icon = icon,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -90,11 +100,12 @@ private fun CollapsedSearchView(
|
|||
onBackClick: () -> Unit,
|
||||
onExpandedChange: (Boolean) -> Unit,
|
||||
title: String? = null,
|
||||
subtitle: String? = null,
|
||||
icon: Painter? = null,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.secondary)
|
||||
.padding(TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
@ -103,25 +114,59 @@ private fun CollapsedSearchView(
|
|||
painter = painterResource(R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens.spacing16)
|
||||
.size(TangemTheme.dimens.size24)
|
||||
.clickable { onBackClick() },
|
||||
tint = MaterialTheme.colors.onPrimary,
|
||||
)
|
||||
if (!title.isNullOrBlank()) {
|
||||
Text(
|
||||
text = title,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
if (!title.isNullOrBlank()) {
|
||||
Text(
|
||||
text = title,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size24),
|
||||
)
|
||||
}
|
||||
if (subtitle != null) {
|
||||
SubtitleView(subtitle = subtitle, icon = icon)
|
||||
}
|
||||
}
|
||||
|
||||
SpacerWMax()
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_search_24),
|
||||
tint = tint,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.clickable { onExpandedChange(true) },
|
||||
.clickable { onExpandedChange(true) }
|
||||
.padding(end = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubtitleView(subtitle: String, icon: Painter?) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
icon?.let {
|
||||
Image(
|
||||
painter = icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(TangemTheme.dimens.size16),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = subtitle,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.caption,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -200,6 +245,8 @@ private fun CollapsedSearchViewPreview() {
|
|||
placeholderSearchText = "Search",
|
||||
onSearchChange = {},
|
||||
onSearchDisplayClose = {},
|
||||
subtitle = "Ethereum",
|
||||
icon = painterResource(id = R.drawable.img_eth_22),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
appCurrency: ProxyFiatCurrency,
|
||||
): List<TokenWithBalance> {
|
||||
return tokens.map {
|
||||
val balance = balances[it.id]
|
||||
val balance = balances[it.symbol]
|
||||
TokenWithBalance(
|
||||
token = it,
|
||||
tokenBalanceData = TokenBalanceData(
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ dependencies {
|
|||
implementation(Compose.material)
|
||||
implementation(Compose.uiTooling)
|
||||
implementation(Compose.coil)
|
||||
implementation(Compose.constraintLayout)
|
||||
|
||||
/** Domain */
|
||||
implementation(project(":features:swap:domain"))
|
||||
|
|
|
|||
|
|
@ -1,23 +1,26 @@
|
|||
package com.tangem.feature.swap.converters
|
||||
|
||||
import com.tangem.feature.swap.domain.models.domain.Currency
|
||||
import com.tangem.feature.swap.domain.models.domain.NetworkInfo
|
||||
import com.tangem.feature.swap.domain.models.ui.FoundTokensState
|
||||
import com.tangem.feature.swap.domain.models.ui.TokenWithBalance
|
||||
import com.tangem.feature.swap.models.Network
|
||||
import com.tangem.feature.swap.models.SwapSelectTokenStateHolder
|
||||
import com.tangem.feature.swap.models.TokenBalanceData
|
||||
import com.tangem.feature.swap.models.TokenToSelect
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
class TokensDataConverter(
|
||||
private val onSearchEntered: (String) -> Unit,
|
||||
private val onTokenSelected: (String) -> Unit,
|
||||
) : Converter<FoundTokensState, SwapSelectTokenStateHolder> {
|
||||
) {
|
||||
|
||||
override fun convert(value: FoundTokensState): SwapSelectTokenStateHolder {
|
||||
fun convertWithNetwork(value: FoundTokensState, network: NetworkInfo): SwapSelectTokenStateHolder {
|
||||
return SwapSelectTokenStateHolder(
|
||||
addedTokens = value.tokensInWallet.map { tokenWithBalanceToTokenToSelect(it) },
|
||||
otherTokens = value.loadedTokens.map { tokenWithBalanceToTokenToSelect(it) },
|
||||
onSearchEntered = onSearchEntered,
|
||||
onTokenSelected = onTokenSelected,
|
||||
network = Network(network.name, network.blockchainId),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -27,6 +30,7 @@ class TokensDataConverter(
|
|||
name = tokenWithBalance.token.name,
|
||||
symbol = tokenWithBalance.token.symbol,
|
||||
iconUrl = tokenWithBalance.token.logoUrl,
|
||||
isNative = tokenWithBalance.token is Currency.NativeToken,
|
||||
addedTokenBalanceData = TokenBalanceData(
|
||||
amount = tokenWithBalance.tokenBalanceData?.amount,
|
||||
amountEquivalent = tokenWithBalance.tokenBalanceData?.amountEquivalent,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.swap.models
|
|||
data class SwapSelectTokenStateHolder(
|
||||
val addedTokens: List<TokenToSelect>,
|
||||
val otherTokens: List<TokenToSelect>,
|
||||
val network: Network,
|
||||
val onSearchEntered: (String) -> Unit,
|
||||
val onTokenSelected: (String) -> Unit,
|
||||
)
|
||||
|
|
@ -12,10 +13,16 @@ data class TokenToSelect(
|
|||
val name: String,
|
||||
val symbol: String,
|
||||
val iconUrl: String,
|
||||
val isNative: Boolean,
|
||||
val available: Boolean = true,
|
||||
val addedTokenBalanceData: TokenBalanceData? = null,
|
||||
)
|
||||
|
||||
data class Network(
|
||||
val name: String,
|
||||
val blockchainId: String,
|
||||
)
|
||||
|
||||
data class TokenBalanceData(
|
||||
val amount: String?,
|
||||
val amountEquivalent: String?,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ sealed interface TransactionCardType {
|
|||
|
||||
data class SendCard(
|
||||
val onAmountChanged: ((String) -> Unit),
|
||||
val onFocusChanged: ((Boolean) -> Unit),
|
||||
) : TransactionCardType
|
||||
|
||||
data class ReceiveCard(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ data class UiActions(
|
|||
val onSearchEntered: (String) -> Unit,
|
||||
val onTokenSelected: (String) -> Unit,
|
||||
val onAmountChanged: (String) -> Unit,
|
||||
val onAmountSelected: (Boolean) -> Unit,
|
||||
val onSwapClick: () -> Unit,
|
||||
val onGivePermissionClick: () -> Unit,
|
||||
val onChangeCardsClicked: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.text.ParagraphIntrinsics
|
||||
import androidx.compose.ui.text.font.createFontFamilyResolver
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Suppress("MagicNumber", "LongMethod")
|
||||
@Composable
|
||||
internal fun AutoSizeTextField(
|
||||
amount: String,
|
||||
onAmountChange: (String) -> Unit,
|
||||
onFocusChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
val textFieldFocusRequester = remember { FocusRequester() }
|
||||
BoxWithConstraints(modifier = modifier.fillMaxWidth()) {
|
||||
var shrunkFontSize = TangemTheme.typography.h2.fontSize
|
||||
val calculateIntrinsics = @Composable {
|
||||
ParagraphIntrinsics(
|
||||
text = amount,
|
||||
style = TangemTheme.typography.h2.copy(
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
fontSize = shrunkFontSize,
|
||||
),
|
||||
density = LocalDensity.current,
|
||||
fontFamilyResolver = createFontFamilyResolver(LocalContext.current),
|
||||
)
|
||||
}
|
||||
|
||||
var intrinsics = calculateIntrinsics()
|
||||
with(LocalDensity.current) {
|
||||
while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) {
|
||||
shrunkFontSize *= 0.9f
|
||||
intrinsics = calculateIntrinsics()
|
||||
}
|
||||
}
|
||||
val customTextSelectionColors = TextSelectionColors(
|
||||
handleColor = Color.Transparent,
|
||||
backgroundColor = TangemTheme.colors.text.secondary.copy(alpha = 0.4f),
|
||||
)
|
||||
CompositionLocalProvider(LocalTextSelectionColors provides customTextSelectionColors) {
|
||||
BasicTextField(
|
||||
value = amount,
|
||||
onValueChange = onAmountChange,
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(textFieldFocusRequester)
|
||||
.onFocusChanged { onFocusChange(it.hasFocus) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
keyboardType = KeyboardType.Decimal,
|
||||
),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
decorationBox = { innerTextField ->
|
||||
if (amount.isBlank()) {
|
||||
Text(
|
||||
text = "0",
|
||||
color = TangemTheme.colors.text.disabled,
|
||||
style = TangemTheme.typography.h2,
|
||||
)
|
||||
}
|
||||
innerTextField()
|
||||
},
|
||||
textStyle = TangemTheme.typography.h2.copy(
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
fontSize = shrunkFontSize,
|
||||
),
|
||||
cursorBrush = SolidColor(TangemTheme.colors.text.primary1),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
networkId = initialCurrency.networkId,
|
||||
blockchainId = networkInfo.blockchainId,
|
||||
sendCardData = SwapCardData(
|
||||
type = TransactionCardType.SendCard(actions.onAmountChanged),
|
||||
type = TransactionCardType.SendCard(actions.onAmountChanged, actions.onAmountSelected),
|
||||
amount = null,
|
||||
amountEquivalent = null,
|
||||
tokenIconUrl = initialCurrency.logoUrl,
|
||||
|
|
@ -205,8 +205,17 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
)
|
||||
}
|
||||
|
||||
fun addTokensToState(uiState: SwapStateHolder, dataState: FoundTokensState): SwapStateHolder {
|
||||
return uiState.copy(selectTokenState = tokensDataConverter.convert(dataState))
|
||||
fun addTokensToState(
|
||||
uiState: SwapStateHolder,
|
||||
dataState: FoundTokensState,
|
||||
networkInfo: NetworkInfo,
|
||||
): SwapStateHolder {
|
||||
return uiState.copy(
|
||||
selectTokenState = tokensDataConverter.convertWithNetwork(
|
||||
value = dataState,
|
||||
network = networkInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertPermissionState(
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ import androidx.compose.ui.res.painterResource
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import com.tangem.core.ui.components.CardWithIcon
|
||||
import com.tangem.core.ui.components.ClickableWarningCard
|
||||
import com.tangem.core.ui.components.Keyboard
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconRight
|
||||
|
|
@ -37,7 +37,7 @@ import com.tangem.core.ui.components.RefreshableWaringCard
|
|||
import com.tangem.core.ui.components.SimpleOkDialog
|
||||
import com.tangem.core.ui.components.SmallInfoCard
|
||||
import com.tangem.core.ui.components.SmallInfoCardWithWarning
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.WarningCard
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.components.keyboardAsState
|
||||
import com.tangem.core.ui.extensions.getActiveIconRes
|
||||
|
|
@ -85,12 +85,7 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
|
|||
|
||||
FeeItem(feeState = state.fee, currency = state.networkCurrency)
|
||||
|
||||
if (state.warnings.isNotEmpty()) {
|
||||
SwapWarnings(
|
||||
warnings = state.warnings,
|
||||
onPermissionWarningClick = onPermissionWarningClick,
|
||||
)
|
||||
}
|
||||
if (state.warnings.isNotEmpty()) SwapWarnings(warnings = state.warnings)
|
||||
|
||||
if (state.permissionState is SwapPermissionState.InProgress) {
|
||||
CardWithIcon(
|
||||
|
|
@ -106,25 +101,7 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (state.warnings.any { it is SwapWarning.InsufficientFunds }) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_insufficient_funds),
|
||||
enabled = false,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
} else {
|
||||
PrimaryButtonIconRight(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_swap),
|
||||
icon = painterResource(id = R.drawable.ic_tangem_24),
|
||||
enabled = state.swapButton.enabled,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
}
|
||||
MainButton(state = state, onPermissionWarningClick = onPermissionWarningClick)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -159,59 +136,71 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
|
|||
|
||||
@Composable
|
||||
private fun MainInfo(state: SwapStateHolder) {
|
||||
Box(
|
||||
ConstraintLayout(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val networkIconRes = remember {
|
||||
getActiveIconRes(state.blockchainId)
|
||||
}
|
||||
Column {
|
||||
TransactionCard(
|
||||
type = state.sendCardData.type,
|
||||
balance = state.sendCardData.balance,
|
||||
amount = state.sendCardData.amount,
|
||||
amountEquivalent = state.sendCardData.amountEquivalent,
|
||||
tokenIconUrl = state.sendCardData.tokenIconUrl,
|
||||
tokenCurrency = state.sendCardData.tokenCurrency,
|
||||
networkIconRes = if (state.sendCardData.isNotNativeToken) networkIconRes else null,
|
||||
iconPlaceholder = state.sendCardData.coinId?.let {
|
||||
getActiveIconResByCoinId(it, state.networkId)
|
||||
},
|
||||
onChangeTokenClick = if (state.sendCardData.canSelectAnotherToken) state.onSelectTokenClick else null,
|
||||
)
|
||||
SpacerH16()
|
||||
TransactionCard(
|
||||
type = state.receiveCardData.type,
|
||||
balance = state.receiveCardData.balance,
|
||||
amount = state.receiveCardData.amount,
|
||||
amountEquivalent = state.receiveCardData.amountEquivalent,
|
||||
tokenIconUrl = state.receiveCardData.tokenIconUrl,
|
||||
tokenCurrency = state.receiveCardData.tokenCurrency,
|
||||
networkIconRes = if (state.receiveCardData.isNotNativeToken) networkIconRes else null,
|
||||
iconPlaceholder = state.receiveCardData.coinId?.let {
|
||||
getActiveIconResByCoinId(it, state.networkId)
|
||||
},
|
||||
onChangeTokenClick = if (state.receiveCardData.canSelectAnotherToken) {
|
||||
state.onSelectTokenClick
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
SwapButton(state)
|
||||
val (topCard, bottomCard, button) = createRefs()
|
||||
TransactionCard(
|
||||
type = state.sendCardData.type,
|
||||
balance = state.sendCardData.balance,
|
||||
amount = state.sendCardData.amount,
|
||||
amountEquivalent = state.sendCardData.amountEquivalent,
|
||||
tokenIconUrl = state.sendCardData.tokenIconUrl,
|
||||
tokenCurrency = state.sendCardData.tokenCurrency,
|
||||
networkIconRes = if (state.sendCardData.isNotNativeToken) networkIconRes else null,
|
||||
iconPlaceholder = state.sendCardData.coinId?.let {
|
||||
getActiveIconResByCoinId(it, state.networkId)
|
||||
},
|
||||
onChangeTokenClick = if (state.sendCardData.canSelectAnotherToken) state.onSelectTokenClick else null,
|
||||
modifier = Modifier.constrainAs(topCard) {
|
||||
top.linkTo(parent.top)
|
||||
},
|
||||
)
|
||||
val marginCard = TangemTheme.dimens.spacing16
|
||||
TransactionCard(
|
||||
type = state.receiveCardData.type,
|
||||
balance = state.receiveCardData.balance,
|
||||
amount = state.receiveCardData.amount,
|
||||
amountEquivalent = state.receiveCardData.amountEquivalent,
|
||||
tokenIconUrl = state.receiveCardData.tokenIconUrl,
|
||||
tokenCurrency = state.receiveCardData.tokenCurrency,
|
||||
networkIconRes = if (state.receiveCardData.isNotNativeToken) networkIconRes else null,
|
||||
iconPlaceholder = state.receiveCardData.coinId?.let {
|
||||
getActiveIconResByCoinId(it, state.networkId)
|
||||
},
|
||||
onChangeTokenClick = if (state.receiveCardData.canSelectAnotherToken) {
|
||||
state.onSelectTokenClick
|
||||
} else {
|
||||
null
|
||||
},
|
||||
modifier = Modifier.constrainAs(bottomCard) {
|
||||
top.linkTo(topCard.bottom, margin = marginCard)
|
||||
},
|
||||
)
|
||||
val marginButton = TangemTheme.dimens.spacing32
|
||||
SwapButton(
|
||||
state,
|
||||
modifier = Modifier.constrainAs(button) {
|
||||
bottom.linkTo(topCard.bottom, margin = -marginButton)
|
||||
start.linkTo(topCard.start)
|
||||
end.linkTo(topCard.end)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun SwapButton(state: SwapStateHolder) {
|
||||
private fun SwapButton(state: SwapStateHolder, modifier: Modifier = Modifier) {
|
||||
Card(
|
||||
elevation = TangemTheme.dimens.elevation3,
|
||||
shape = CircleShape,
|
||||
backgroundColor = TangemTheme.colors.background.plain,
|
||||
contentColor = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.size(TangemTheme.dimens.size48),
|
||||
modifier = modifier.size(TangemTheme.dimens.size48),
|
||||
onClick = state.onChangeCardsClicked,
|
||||
enabled = !state.updateInProgress,
|
||||
) {
|
||||
|
|
@ -262,7 +251,6 @@ private fun FeeItem(feeState: FeeState, currency: String) {
|
|||
@Composable
|
||||
private fun SwapWarnings(
|
||||
warnings: List<SwapWarning>,
|
||||
onPermissionWarningClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -275,7 +263,7 @@ private fun SwapWarnings(
|
|||
// WarningCard(title = stringResource(id = R.string.), description = stringResource(id = R.string.))
|
||||
// }
|
||||
is SwapWarning.PermissionNeeded -> {
|
||||
ClickableWarningCard(
|
||||
WarningCard(
|
||||
title = stringResource(id = R.string.swapping_permission_header),
|
||||
description = stringResource(
|
||||
id = R.string.swapping_permission_subheader,
|
||||
|
|
@ -288,7 +276,6 @@ private fun SwapWarnings(
|
|||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
)
|
||||
},
|
||||
onClick = onPermissionWarningClick,
|
||||
)
|
||||
}
|
||||
is SwapWarning.GenericWarning -> {
|
||||
|
|
@ -311,10 +298,44 @@ private fun SwapWarnings(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> Unit) {
|
||||
when {
|
||||
state.warnings.any { it is SwapWarning.PermissionNeeded } -> {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_give_permission),
|
||||
enabled = true,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = onPermissionWarningClick,
|
||||
)
|
||||
}
|
||||
state.warnings.any { it is SwapWarning.InsufficientFunds } -> {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_insufficient_funds),
|
||||
enabled = false,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
PrimaryButtonIconRight(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_swap),
|
||||
icon = painterResource(id = R.drawable.ic_tangem_24),
|
||||
enabled = state.swapButton.enabled,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region preview
|
||||
|
||||
private val sendCard = SwapCardData(
|
||||
type = TransactionCardType.SendCard {},
|
||||
type = TransactionCardType.SendCard({}) {},
|
||||
amount = "1 000 000",
|
||||
amountEquivalent = "1 000 000",
|
||||
tokenIconUrl = "",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -24,6 +25,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -32,7 +34,9 @@ import coil.request.ImageRequest
|
|||
import com.tangem.core.ui.components.CurrencyPlaceholderIcon
|
||||
import com.tangem.core.ui.components.SpacerW2
|
||||
import com.tangem.core.ui.components.appbar.ExpandableSearchView
|
||||
import com.tangem.core.ui.extensions.getActiveIconRes
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.Network
|
||||
import com.tangem.feature.swap.models.SwapSelectTokenStateHolder
|
||||
import com.tangem.feature.swap.models.TokenBalanceData
|
||||
import com.tangem.feature.swap.models.TokenToSelect
|
||||
|
|
@ -53,6 +57,8 @@ fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () -> Unit)
|
|||
placeholderSearchText = stringResource(id = R.string.search_tokens_title),
|
||||
onSearchChange = state.onSearchEntered,
|
||||
onSearchDisplayClose = { state.onSearchEntered("") },
|
||||
subtitle = state.network.name,
|
||||
icon = painterResource(id = getActiveIconRes(state.network.blockchainId)),
|
||||
)
|
||||
},
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
|
|
@ -69,10 +75,12 @@ private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier =
|
|||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
stickyHeader { Header(title = R.string.swapping_token_list_your_tokens) }
|
||||
if (state.addedTokens.isNotEmpty()) {
|
||||
stickyHeader { Header(title = R.string.swapping_token_list_your_tokens) }
|
||||
}
|
||||
|
||||
itemsIndexed(items = state.addedTokens) { index, item ->
|
||||
TokenItem(token = item, onTokenClick = { state.onTokenSelected(item.id) })
|
||||
TokenItem(token = item, network = state.network, onTokenClick = { state.onTokenSelected(item.id) })
|
||||
|
||||
if (index != state.addedTokens.lastIndex) {
|
||||
Divider(
|
||||
|
|
@ -82,10 +90,12 @@ private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier =
|
|||
}
|
||||
}
|
||||
|
||||
stickyHeader { Header(title = R.string.swapping_token_list_other_tokens) }
|
||||
if (state.otherTokens.isNotEmpty()) {
|
||||
stickyHeader { Header(title = R.string.swapping_token_list_other_tokens) }
|
||||
}
|
||||
|
||||
itemsIndexed(items = state.otherTokens) { index, item ->
|
||||
TokenItem(token = item, onTokenClick = { state.onTokenSelected(item.id) })
|
||||
TokenItem(token = item, network = state.network, onTokenClick = { state.onTokenSelected(item.id) })
|
||||
if (index != state.otherTokens.lastIndex) {
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
|
|
@ -112,8 +122,9 @@ private fun Header(@StringRes title: Int) {
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun TokenItem(token: TokenToSelect, onTokenClick: () -> Unit) {
|
||||
private fun TokenItem(token: TokenToSelect, network: Network, onTokenClick: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -124,7 +135,10 @@ private fun TokenItem(token: TokenToSelect, onTokenClick: () -> Unit) {
|
|||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TokenIcon(token = token)
|
||||
TokenIcon(
|
||||
token = token,
|
||||
iconPlaceholder = if (token.isNative) getActiveIconRes(network.blockchainId) else null,
|
||||
)
|
||||
|
||||
Column(modifier = Modifier.align(Alignment.CenterVertically)) {
|
||||
Text(
|
||||
|
|
@ -176,7 +190,13 @@ private fun TokenItem(token: TokenToSelect, onTokenClick: () -> Unit) {
|
|||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun TokenIcon(token: TokenToSelect) {
|
||||
private fun TokenIcon(
|
||||
token: TokenToSelect,
|
||||
@DrawableRes iconPlaceholder: Int?,
|
||||
) {
|
||||
val data = token.iconUrl.ifEmpty {
|
||||
iconPlaceholder
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(end = TangemTheme.dimens.spacing12),
|
||||
|
|
@ -192,7 +212,7 @@ private fun TokenIcon(token: TokenToSelect) {
|
|||
SubcomposeAsyncImage(
|
||||
modifier = iconModifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(token.iconUrl)
|
||||
.data(data)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = token.id,
|
||||
|
|
@ -225,6 +245,7 @@ private val token = TokenToSelect(
|
|||
name = "USDC",
|
||||
symbol = "USDC",
|
||||
iconUrl = "",
|
||||
isNative = false,
|
||||
addedTokenBalanceData = TokenBalanceData(
|
||||
amount = "15 000 $",
|
||||
amountEquivalent = "15 000 " +
|
||||
|
|
@ -241,6 +262,7 @@ private fun TokenScreenPreview() {
|
|||
otherTokens = listOf(token, token, token),
|
||||
onSearchEntered = {},
|
||||
onTokenSelected = {},
|
||||
network = Network("Ethereum", "ETH"),
|
||||
),
|
||||
onBack = {},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,10 +7,8 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
|
|
@ -21,35 +19,19 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Color.Companion.Transparent
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.ParagraphIntrinsics
|
||||
import androidx.compose.ui.text.font.createFontFamilyResolver
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -60,7 +42,8 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.components.FontSizeRange
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
import com.tangem.core.ui.components.SpacerH4
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.core.ui.components.SpacerW16
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.TransactionCardType
|
||||
import com.valentinilk.shimmer.shimmer
|
||||
|
|
@ -70,10 +53,11 @@ import com.valentinilk.shimmer.shimmer
|
|||
fun TransactionCard(
|
||||
type: TransactionCardType,
|
||||
balance: String,
|
||||
amount: String?,
|
||||
amountEquivalent: String?,
|
||||
tokenIconUrl: String,
|
||||
tokenCurrency: String,
|
||||
amount: String?,
|
||||
amountEquivalent: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
@DrawableRes iconPlaceholder: Int? = null,
|
||||
@DrawableRes networkIconRes: Int? = null,
|
||||
onChangeTokenClick: (() -> Unit)? = null,
|
||||
|
|
@ -82,13 +66,13 @@ fun TransactionCard(
|
|||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size116),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Header(balance = balance, type = type)
|
||||
|
|
@ -155,14 +139,17 @@ private fun Header(
|
|||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.subtitle2,
|
||||
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size24),
|
||||
)
|
||||
SpacerWMax(Modifier.defaultMinSize(minWidth = TangemTheme.dimens.spacing16))
|
||||
SpacerW16()
|
||||
if (balance.isNotBlank()) {
|
||||
Text(
|
||||
text = stringResource(R.string.common_balance, balance),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.body2,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = TangemTheme.dimens.size20)
|
||||
.padding(top = TangemTheme.dimens.spacing2),
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
|
|
@ -188,21 +175,21 @@ private fun Content(
|
|||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing8,
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
end = TangemTheme.dimens.spacing92,
|
||||
),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
val sumTextModifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size32)
|
||||
when (type) {
|
||||
is TransactionCardType.ReceiveCard -> {
|
||||
if (amount != null) {
|
||||
|
|
@ -211,10 +198,12 @@ private fun Content(
|
|||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.h2,
|
||||
fontSizeRange = FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize),
|
||||
modifier = sumTextModifier,
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing4)
|
||||
.size(width = TangemTheme.dimens.size102, height = TangemTheme.dimens.size24)
|
||||
.shimmer()
|
||||
.background(
|
||||
|
|
@ -226,28 +215,27 @@ private fun Content(
|
|||
}
|
||||
is TransactionCardType.SendCard -> {
|
||||
AutoSizeTextField(
|
||||
modifier = sumTextModifier,
|
||||
amount = amount ?: "",
|
||||
onAmountChange = { type.onAmountChanged(it) },
|
||||
onFocusChange = type.onFocusChanged,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size4)
|
||||
.weight(1f),
|
||||
)
|
||||
SpacerH8()
|
||||
|
||||
if (amountEquivalent != null) {
|
||||
Text(
|
||||
text = amountEquivalent,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size20),
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing4)
|
||||
.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12)
|
||||
.shimmer()
|
||||
.background(
|
||||
|
|
@ -260,71 +248,6 @@ private fun Content(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun AutoSizeTextField(amount: String, onAmountChange: (String) -> Unit) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
val textFieldFocusRequester = remember { FocusRequester() }
|
||||
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
|
||||
var shrunkFontSize = TangemTheme.typography.h2.fontSize
|
||||
val calculateIntrinsics = @Composable {
|
||||
ParagraphIntrinsics(
|
||||
text = amount,
|
||||
style = TangemTheme.typography.h2.copy(
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
fontSize = shrunkFontSize,
|
||||
),
|
||||
density = LocalDensity.current,
|
||||
fontFamilyResolver = createFontFamilyResolver(LocalContext.current),
|
||||
)
|
||||
}
|
||||
|
||||
var intrinsics = calculateIntrinsics()
|
||||
|
||||
with(LocalDensity.current) {
|
||||
while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) {
|
||||
shrunkFontSize *= 0.9f
|
||||
intrinsics = calculateIntrinsics()
|
||||
}
|
||||
}
|
||||
val customTextSelectionColors = TextSelectionColors(
|
||||
handleColor = Transparent,
|
||||
backgroundColor = TangemTheme.colors.text.secondary.copy(alpha = 0.4f),
|
||||
)
|
||||
CompositionLocalProvider(LocalTextSelectionColors provides customTextSelectionColors) {
|
||||
BasicTextField(
|
||||
value = amount,
|
||||
onValueChange = onAmountChange,
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(textFieldFocusRequester),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
keyboardType = KeyboardType.Decimal,
|
||||
),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
decorationBox = { innerTextField ->
|
||||
if (amount.isBlank()) {
|
||||
Text(
|
||||
text = "0",
|
||||
color = TangemTheme.colors.text.disabled,
|
||||
style = TangemTheme.typography.h2,
|
||||
)
|
||||
}
|
||||
innerTextField()
|
||||
},
|
||||
textStyle = TangemTheme.typography.h2.copy(
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
fontSize = shrunkFontSize,
|
||||
),
|
||||
cursorBrush = SolidColor(TangemTheme.colors.text.primary1),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
fun Token(
|
||||
|
|
@ -452,7 +375,7 @@ private fun Preview_SwapMainCard_InDarkTheme() {
|
|||
@Composable
|
||||
private fun TransactionCardPreview() {
|
||||
TransactionCard(
|
||||
type = TransactionCardType.SendCard {},
|
||||
type = TransactionCardType.SendCard({}) {},
|
||||
amount = "1 000 000",
|
||||
amountEquivalent = "1 000 000",
|
||||
tokenIconUrl = "",
|
||||
|
|
|
|||
|
|
@ -123,7 +123,11 @@ internal class SwapViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun updateTokensState(dataState: FoundTokensState) {
|
||||
uiState = stateBuilder.addTokensToState(uiState, dataState)
|
||||
uiState = stateBuilder.addTokensToState(
|
||||
uiState = uiState,
|
||||
dataState = dataState,
|
||||
networkInfo = blockchainInteractor.getBlockchainInfo(currency.networkId),
|
||||
)
|
||||
}
|
||||
|
||||
private fun startLoadingQuotes(fromToken: Currency, toToken: Currency, amount: String) {
|
||||
|
|
@ -330,6 +334,13 @@ internal class SwapViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private fun onAmountSelected(selected: Boolean) {
|
||||
if (selected) {
|
||||
analyticsEventHandler.send(SwapEvents.SendTokenBalanceClicked)
|
||||
}
|
||||
}
|
||||
|
||||
private fun cutAmountWithDecimals(maxDecimals: Int, amount: String): String {
|
||||
return getValidatedNumberWithFixedDecimals(amount, maxDecimals)
|
||||
}
|
||||
|
|
@ -377,6 +388,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
hidePermissionBottomSheet = {
|
||||
analyticsEventHandler.send(SwapEvents.ButtonPermissionCancelClicked)
|
||||
},
|
||||
onAmountSelected = { onAmountSelected(it) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue