From 6ac57c67fd85923a104f15151d4be07fb32cc45f Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 27 Jan 2023 18:00:19 +0400 Subject: [PATCH 1/3] Updated on 2026-08-14 --- buildSrc/src/main/java/Dependency.kt | 1 + buildSrc/src/main/java/Versions.kt | 1 + .../ui/components/appbar/AppBarWithSearch.kt | 67 ++++++-- features/swap/presentation/build.gradle.kts | 1 + .../swap/converters/TokensDataConverter.kt | 10 ++ .../swap/models/SwapSelectTokenStateHolder.kt | 6 + .../feature/swap/models/SwapStateHolder.kt | 1 + .../tangem/feature/swap/models/UiActions.kt | 1 + .../feature/swap/ui/AutosizeTextField.kt | 96 +++++++++++ .../tangem/feature/swap/ui/StateBuilder.kt | 9 +- .../feature/swap/ui/SwapScreenContent.kt | 159 ++++++++++-------- .../feature/swap/ui/SwapSelectTokenScreen.kt | 14 +- .../tangem/feature/swap/ui/TransactionCard.kt | 127 +++----------- .../feature/swap/viewmodels/SwapViewModel.kt | 5 + 14 files changed, 313 insertions(+), 185 deletions(-) create mode 100644 features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/AutosizeTextField.kt diff --git a/buildSrc/src/main/java/Dependency.kt b/buildSrc/src/main/java/Dependency.kt index cee8ea2a90..af09699d8f 100644 --- a/buildSrc/src/main/java/Dependency.kt +++ b/buildSrc/src/main/java/Dependency.kt @@ -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 { diff --git a/buildSrc/src/main/java/Versions.kt b/buildSrc/src/main/java/Versions.kt index 4c6cb088ef..19dc1d08e2 100644 --- a/buildSrc/src/main/java/Versions.kt +++ b/buildSrc/src/main/java/Versions.kt @@ -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 diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt index ba83e98119..b10c630c32 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt @@ -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 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), ) } } diff --git a/features/swap/presentation/build.gradle.kts b/features/swap/presentation/build.gradle.kts index aeeaf50f3b..684ec4d57c 100644 --- a/features/swap/presentation/build.gradle.kts +++ b/features/swap/presentation/build.gradle.kts @@ -65,6 +65,7 @@ dependencies { implementation(Compose.material) implementation(Compose.uiTooling) implementation(Compose.coil) + implementation(Compose.constraintLayout) /** Domain */ implementation(project(":features:swap:domain")) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt index bc67135496..16443ce5cc 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt @@ -2,6 +2,7 @@ package com.tangem.feature.swap.converters 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 @@ -12,12 +13,21 @@ class TokensDataConverter( private val onTokenSelected: (String) -> Unit, ) : Converter { + private var networkName: String? = null + private var blockchainId: String? = null + + fun setNetworkData(networkName: String, blockchainId: String) { + this.networkName = networkName + this.blockchainId = blockchainId + } + override fun convert(value: FoundTokensState): SwapSelectTokenStateHolder { return SwapSelectTokenStateHolder( addedTokens = value.tokensInWallet.map { tokenWithBalanceToTokenToSelect(it) }, otherTokens = value.loadedTokens.map { tokenWithBalanceToTokenToSelect(it) }, onSearchEntered = onSearchEntered, onTokenSelected = onTokenSelected, + network = Network(networkName ?: "", blockchainId ?: ""), ) } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt index be315efabf..9a79105dea 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt @@ -3,6 +3,7 @@ package com.tangem.feature.swap.models data class SwapSelectTokenStateHolder( val addedTokens: List, val otherTokens: List, + val network: Network, val onSearchEntered: (String) -> Unit, val onTokenSelected: (String) -> Unit, ) @@ -16,6 +17,11 @@ data class TokenToSelect( val addedTokenBalanceData: TokenBalanceData? = null, ) +data class Network( + val name: String, + val blockchainId: String, +) + data class TokenBalanceData( val amount: String?, val amountEquivalent: String?, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt index b2345dbff9..2bd831273c 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt @@ -62,6 +62,7 @@ sealed interface TransactionCardType { data class SendCard( val onAmountChanged: ((String) -> Unit), + val onFocusChanged: ((Boolean) -> Unit), ) : TransactionCardType data class ReceiveCard( diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt index d5fe7389f1..5273bc3b30 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt @@ -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, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/AutosizeTextField.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/AutosizeTextField.kt new file mode 100644 index 0000000000..7ffc2cc4f1 --- /dev/null +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/AutosizeTextField.kt @@ -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), + ) + } + } +} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 4fd3e2b220..dca8404141 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -30,11 +30,12 @@ internal class StateBuilder(val actions: UiActions) { private val tokensDataConverter = TokensDataConverter(actions.onSearchEntered, actions.onTokenSelected) fun createInitialLoadingState(initialCurrency: Currency, networkInfo: NetworkInfo): SwapStateHolder { + tokensDataConverter.setNetworkData(networkInfo.name, networkInfo.blockchainId) return SwapStateHolder( 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, @@ -206,7 +207,11 @@ internal class StateBuilder(val actions: UiActions) { } fun addTokensToState(uiState: SwapStateHolder, dataState: FoundTokensState): SwapStateHolder { - return uiState.copy(selectTokenState = tokensDataConverter.convert(dataState)) + return uiState.copy( + selectTokenState = tokensDataConverter.convert( + value = dataState, + ), + ) } private fun convertPermissionState( diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt index 163635c16d..2d173cddbe 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt @@ -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, - 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 = "", diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt index 30dca38d3a..a19e8f7d1d 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt @@ -24,6 +24,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 +33,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 +56,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,7 +74,9 @@ 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) }) @@ -82,7 +89,9 @@ 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) }) @@ -241,6 +250,7 @@ private fun TokenScreenPreview() { otherTokens = listOf(token, token, token), onSearchEntered = {}, onTokenSelected = {}, + network = Network("Ethereum", "ETH"), ), onBack = {}, ) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt index cd753171ee..4a62b9f5d0 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt @@ -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 = "", diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index 0b631ab000..5ec0265f58 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -330,6 +330,10 @@ internal class SwapViewModel @Inject constructor( } } + @Suppress("UnusedPrivateMember") + private fun onAmountSelected(selected: Boolean) { // TODO + } + private fun cutAmountWithDecimals(maxDecimals: Int, amount: String): String { return getValidatedNumberWithFixedDecimals(amount, maxDecimals) } @@ -377,6 +381,7 @@ internal class SwapViewModel @Inject constructor( hidePermissionBottomSheet = { analyticsEventHandler.send(SwapEvents.ButtonPermissionCancelClicked) }, + onAmountSelected = { onAmountSelected(it) }, ) } From d112773ff05ba79cb63bb6f773418749b928ed58 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 30 Jan 2023 10:55:23 +0300 Subject: [PATCH 2/3] Updated on 2026-08-14 --- .../feature/swap/domain/SwapInteractorImpl.kt | 2 +- .../swap/converters/TokensDataConverter.kt | 18 +++++--------- .../swap/models/SwapSelectTokenStateHolder.kt | 1 + .../tangem/feature/swap/ui/StateBuilder.kt | 10 +++++--- .../feature/swap/ui/SwapSelectTokenScreen.kt | 24 ++++++++++++++----- .../feature/swap/viewmodels/SwapViewModel.kt | 11 +++++++-- 6 files changed, 42 insertions(+), 24 deletions(-) diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 340adffa9c..2fcdcc5b52 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -266,7 +266,7 @@ internal class SwapInteractorImpl @Inject constructor( appCurrency: ProxyFiatCurrency, ): List { return tokens.map { - val balance = balances[it.id] + val balance = balances[it.symbol] TokenWithBalance( token = it, tokenBalanceData = TokenBalanceData( diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt index 16443ce5cc..7491904442 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt @@ -1,33 +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 { +) { - private var networkName: String? = null - private var blockchainId: String? = null - - fun setNetworkData(networkName: String, blockchainId: String) { - this.networkName = networkName - this.blockchainId = blockchainId - } - - 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(networkName ?: "", blockchainId ?: ""), + network = Network(network.name, network.blockchainId), ) } @@ -37,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, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt index 9a79105dea..b51831dedb 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt @@ -13,6 +13,7 @@ data class TokenToSelect( val name: String, val symbol: String, val iconUrl: String, + val isNative: Boolean, val available: Boolean = true, val addedTokenBalanceData: TokenBalanceData? = null, ) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index dca8404141..0d24f31906 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -30,7 +30,6 @@ internal class StateBuilder(val actions: UiActions) { private val tokensDataConverter = TokensDataConverter(actions.onSearchEntered, actions.onTokenSelected) fun createInitialLoadingState(initialCurrency: Currency, networkInfo: NetworkInfo): SwapStateHolder { - tokensDataConverter.setNetworkData(networkInfo.name, networkInfo.blockchainId) return SwapStateHolder( networkId = initialCurrency.networkId, blockchainId = networkInfo.blockchainId, @@ -206,10 +205,15 @@ internal class StateBuilder(val actions: UiActions) { ) } - fun addTokensToState(uiState: SwapStateHolder, dataState: FoundTokensState): SwapStateHolder { + fun addTokensToState( + uiState: SwapStateHolder, + dataState: FoundTokensState, + networkInfo: NetworkInfo, + ): SwapStateHolder { return uiState.copy( - selectTokenState = tokensDataConverter.convert( + selectTokenState = tokensDataConverter.convertWithNetwork( value = dataState, + network = networkInfo, ), ) } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt index a19e8f7d1d..f086d605b3 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt @@ -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 @@ -79,7 +80,7 @@ private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier = } 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( @@ -94,7 +95,7 @@ private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier = } 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, @@ -121,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() @@ -133,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( @@ -185,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), @@ -201,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, @@ -234,6 +245,7 @@ private val token = TokenToSelect( name = "USDC", symbol = "USDC", iconUrl = "", + isNative = false, addedTokenBalanceData = TokenBalanceData( amount = "15 000 $", amountEquivalent = "15 000 " + diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index 5ec0265f58..1056c842c6 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -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) { @@ -331,7 +335,10 @@ internal class SwapViewModel @Inject constructor( } @Suppress("UnusedPrivateMember") - private fun onAmountSelected(selected: Boolean) { // TODO + private fun onAmountSelected(selected: Boolean) { + if (selected) { + analyticsEventHandler.send(SwapEvents.SendTokenBalanceClicked) + } } private fun cutAmountWithDecimals(maxDecimals: Int, amount: String): String { From de6cccdf8b99159622e110e1053970c640169902 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 30 Jan 2023 16:44:45 +0400 Subject: [PATCH 3/3] Updated on 2026-08-14 --- buildSrc/src/main/java/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/Versions.kt b/buildSrc/src/main/java/Versions.kt index 19dc1d08e2..7a98a8bbb0 100644 --- a/buildSrc/src/main/java/Versions.kt +++ b/buildSrc/src/main/java/Versions.kt @@ -61,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"