Updated on 2026-08-14

This commit is contained in:
Tangem 2023-01-23 14:30:32 +03:00
commit 8f78031374
13 changed files with 118 additions and 51 deletions

View file

@ -17,7 +17,7 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_trade"
style="@style/TapPrimaryIconButton"
android:text="@string/wallet_button_trade"
android:text="@string/wallet_button_actions"
android:visibility="gone"
app:icon="@drawable/ic_arrows_up_down"
tools:visibility="visible" />

View file

@ -237,6 +237,7 @@
<string name="common_cancel">Abbrechen</string>
<string name="common_accept">Akzeptieren</string>
<string name="common_camera_denied_alert_message">Sie haben keinen Zugang zur Kamera erteilt, bitte passen Sie Ihre Datenschutzeinstellungen an</string>
<string name="wallet_button_actions">Actions</string>
<string name="wallet_button_send">Absenden</string>
<string name="wallet_button_create_wallet">ein Wallet erstellen</string>
<string name="wallet_title">Tangem</string>

View file

@ -237,6 +237,7 @@
<string name="common_cancel">Annuler</string>
<string name="common_accept">J\'accepte</string>
<string name="common_camera_denied_alert_message">Vous n\'avez pas octroyé l\'accès à votre caméra, veuillez modifier vos paramètres de confidentialité</string>
<string name="wallet_button_actions">Actions</string>
<string name="wallet_button_send">Envoyer</string>
<string name="wallet_button_create_wallet">Créer un portefeuille</string>
<string name="wallet_title">Tangem</string>

View file

@ -237,6 +237,7 @@
<string name="common_cancel">Annulla</string>
<string name="common_accept">Accetta</string>
<string name="common_camera_denied_alert_message">Non hai fornito l\'accesso alla tua videocamera, modifica le tue impostazioni sulla privacy</string>
<string name="wallet_button_actions">Actions</string>
<string name="wallet_button_send">Invia</string>
<string name="wallet_button_create_wallet">Crea portafoglio</string>
<string name="wallet_title">Tangem</string>

View file

@ -237,6 +237,7 @@
<string name="common_cancel">Отмена</string>
<string name="common_accept">Принять</string>
<string name="common_camera_denied_alert_message">Вы не предоставили доступ к камере, пожалуйста, измените настройки конфиденциальности.</string>
<string name="wallet_button_actions">Действия</string>
<string name="wallet_button_send">Отправить</string>
<string name="wallet_button_create_wallet">Создать кошелек</string>
<string name="wallet_title">Tangem</string>

View file

@ -237,6 +237,7 @@
<string name="common_cancel">Cancel</string>
<string name="common_accept">Accept</string>
<string name="common_camera_denied_alert_message">You have not given access to your camera, please adjust your privacy settings</string>
<string name="wallet_button_actions">Actions</string>
<string name="wallet_button_send">Send</string>
<string name="wallet_button_create_wallet">Create wallet</string>
<string name="wallet_title">Tangem</string>

View file

@ -136,7 +136,7 @@ private fun WarningCardSurface(
shape = RoundedCornerShape(TangemTheme.dimens.size12),
backgroundColor = TangemTheme.colors.background.primary,
elevation = TangemTheme.dimens.elevation2,
onClick = { onClick ?: Unit },
onClick = onClick ?: {},
enabled = onClick != null,
) {
content()

View file

@ -14,7 +14,8 @@ class TokensDataConverter(
override fun convert(value: FoundTokensState): SwapSelectTokenStateHolder {
return SwapSelectTokenStateHolder(
tokens = (value.tokensInWallet + value.loadedTokens).map { tokenWithBalanceToTokenToSelect(it) },
addedTokens = value.tokensInWallet.map { tokenWithBalanceToTokenToSelect(it) },
otherTokens = value.loadedTokens.map { tokenWithBalanceToTokenToSelect(it) },
onSearchEntered = onSearchEntered,
onTokenSelected = onTokenSelected,
)

View file

@ -1,7 +1,8 @@
package com.tangem.feature.swap.models
data class SwapSelectTokenStateHolder(
val tokens: List<TokenToSelect>,
val addedTokens: List<TokenToSelect>,
val otherTokens: List<TokenToSelect>,
val onSearchEntered: (String) -> Unit,
val onTokenSelected: (String) -> Unit,
)

View file

@ -9,6 +9,7 @@ data class SwapStateHolder(
val fee: FeeState = FeeState.Empty,
val warnings: List<SwapWarning> = emptyList(),
val alert: SwapWarning.GenericWarning? = null,
val updateInProgress: Boolean = false,
val permissionState: SwapPermissionState = SwapPermissionState.Empty,
val successState: SwapSuccessStateHolder? = null,

View file

@ -78,7 +78,7 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
if (state.warnings.isNotEmpty()) {
SwapWarnings(
warnings = state.warnings,
onApproveWarningClick = onPermissionWarningClick,
onPermissionWarningClick = onPermissionWarningClick,
)
}
@ -119,8 +119,7 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
AnimatedVisibility(
visible = keyboard is Keyboard.Opened,
modifier = Modifier
.align(Alignment.BottomCenter),
modifier = Modifier.align(Alignment.BottomCenter),
) {
Text(
text = stringResource(id = R.string.send_max_amount_label),
@ -147,12 +146,10 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick:
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun MainInfo(state: SwapStateHolder) {
Box(
modifier = Modifier
.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
Column {
@ -182,21 +179,36 @@ private fun MainInfo(state: SwapStateHolder) {
},
)
}
Card(
elevation = TangemTheme.dimens.elevation3,
shape = CircleShape,
backgroundColor = TangemTheme.colors.background.plain,
contentColor = TangemTheme.colors.text.primary1,
modifier = Modifier
.size(TangemTheme.dimens.size48),
onClick = state.onChangeCardsClicked,
) {
SwapButton()
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun SwapButton() {
Card(
elevation = TangemTheme.dimens.elevation3,
shape = CircleShape,
backgroundColor = TangemTheme.colors.background.plain,
contentColor = TangemTheme.colors.text.primary1,
modifier = Modifier.size(TangemTheme.dimens.size48),
onClick = state.onChangeCardsClicked,
enabled = !state.updateInProgress,
) {
if (state.updateInProgress) {
CircularProgressIndicator(
modifier = Modifier
.size(TangemTheme.dimens.size16)
.padding(TangemTheme.dimens.spacing14),
color = TangemTheme.colors.icon.primary1,
strokeWidth = TangemTheme.dimens.size2,
)
} else {
Icon(
painter = painterResource(id = com.tangem.core.ui.R.drawable.ic_exchange_vertical_24),
painter = painterResource(id = R.drawable.ic_exchange_vertical_24),
contentDescription = null,
tint = TangemTheme.colors.text.primary1,
modifier = Modifier
.padding(TangemTheme.dimens.spacing12),
modifier = Modifier.padding(TangemTheme.dimens.spacing12),
)
}
}
@ -226,7 +238,7 @@ private fun FeeItem(feeState: FeeState, currency: String) {
@Composable
private fun SwapWarnings(
warnings: List<SwapWarning>,
onApproveWarningClick: () -> Unit,
onPermissionWarningClick: () -> Unit,
) {
Column(
modifier = Modifier
@ -252,7 +264,7 @@ private fun SwapWarnings(
modifier = Modifier.size(TangemTheme.dimens.size20),
)
},
onClick = onApproveWarningClick,
onClick = onPermissionWarningClick,
)
}
is SwapWarning.GenericWarning -> {

View file

@ -1,5 +1,7 @@
package com.tangem.feature.swap.ui
import androidx.annotation.StringRes
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@ -23,6 +25,7 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
@ -57,6 +60,7 @@ fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () -> Unit)
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier = Modifier) {
LazyColumn(
@ -64,12 +68,25 @@ private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier =
.background(color = TangemTheme.colors.background.secondary)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
itemsIndexed(items = state.tokens) { index, item ->
stickyHeader { Header(title = R.string.swapping_token_list_your_tokens) }
itemsIndexed(items = state.addedTokens) { index, item ->
TokenItem(token = item, onTokenClick = { state.onTokenSelected(item.id) })
if (index != state.tokens.lastIndex) {
if (index != state.addedTokens.lastIndex) {
Divider(
color = TangemTheme.colors.stroke.primary,
startIndent = TangemTheme.dimens.spacing54,
)
}
}
stickyHeader { Header(title = R.string.swapping_token_list_other_tokens) }
itemsIndexed(items = state.otherTokens) { index, item ->
TokenItem(token = item, onTokenClick = { state.onTokenSelected(item.id) })
if (index != state.otherTokens.lastIndex) {
Divider(
color = TangemTheme.colors.stroke.primary,
startIndent = TangemTheme.dimens.spacing54,
@ -79,6 +96,22 @@ private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier =
}
}
@Composable
private fun Header(@StringRes title: Int) {
Text(
text = stringResource(id = title).uppercase(),
style = TangemTheme.typography.overline,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier
.fillMaxWidth()
.padding(
vertical = TangemTheme.dimens.spacing6,
horizontal = TangemTheme.dimens.spacing16,
),
textAlign = TextAlign.Start,
)
}
@Composable
private fun TokenItem(token: TokenToSelect, onTokenClick: () -> Unit) {
Row(
@ -199,14 +232,13 @@ private val token = TokenToSelect(
),
)
private val tokenNotAvailable = token.copy(available = false)
@Preview
@Composable
private fun TokenScreenPreview() {
SwapSelectTokenScreen(
state = SwapSelectTokenStateHolder(
tokens = listOf(token, tokenNotAvailable, token),
addedTokens = listOf(token, token, token),
otherTokens = listOf(token, token, token),
onSearchEntered = {},
onTokenSelected = {},
),

View file

@ -44,7 +44,6 @@ 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.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.ParagraphIntrinsics
@ -61,6 +60,7 @@ 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.res.TangemTheme
import com.tangem.feature.swap.models.TransactionCardType
import com.valentinilk.shimmer.shimmer
@ -136,7 +136,8 @@ private fun Header(
modifier = modifier
.fillMaxWidth()
.padding(
bottom = dimensionResource(id = R.dimen.spacing8),
bottom = TangemTheme.dimens.spacing8,
top = TangemTheme.dimens.spacing12,
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing16,
),
@ -152,15 +153,26 @@ private fun Header(
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
style = MaterialTheme.typography.subtitle2,
modifier = Modifier.padding(top = TangemTheme.dimens.spacing12),
)
Text(
text = stringResource(R.string.common_balance, balance),
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(top = TangemTheme.dimens.spacing12),
)
SpacerWMax(Modifier.defaultMinSize(minWidth = TangemTheme.dimens.spacing16))
if (balance.isNotBlank()) {
Text(
text = stringResource(R.string.common_balance, balance),
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
style = MaterialTheme.typography.body2,
)
} else {
Box(
modifier = Modifier
.size(width = TangemTheme.dimens.size80, height = TangemTheme.dimens.size12)
.shimmer()
.background(
color = TangemTheme.colors.button.secondary,
shape = RoundedCornerShape(TangemTheme.dimens.radius3),
),
)
}
}
}
@ -196,8 +208,7 @@ private fun Content(
text = amount,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.h2,
fontSizeRange =
FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize),
fontSizeRange = FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize),
)
} else {
Box(
@ -213,7 +224,7 @@ private fun Content(
}
is TransactionCardType.SendCard -> {
AutoSizeTextField(
amount = amount ?: "1",
amount = amount ?: "",
onAmountChange = { type.onAmountChanged(it) },
)
}
@ -279,9 +290,7 @@ private fun AutoSizeTextField(amount: String, onAmountChange: (String) -> Unit)
handleColor = Transparent,
backgroundColor = TangemTheme.colors.text.secondary.copy(alpha = 0.4f),
)
CompositionLocalProvider(
LocalTextSelectionColors provides customTextSelectionColors,
) {
CompositionLocalProvider(LocalTextSelectionColors provides customTextSelectionColors) {
BasicTextField(
value = amount,
onValueChange = onAmountChange,
@ -293,11 +302,17 @@ private fun AutoSizeTextField(amount: String, onAmountChange: (String) -> Unit)
imeAction = ImeAction.Done,
keyboardType = KeyboardType.Decimal,
),
keyboardActions = KeyboardActions(
onDone = {
focusManager.clearFocus()
},
),
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,