Updated on 2026-08-14
This commit is contained in:
commit
036e031c1f
1212 changed files with 28739 additions and 8811 deletions
|
|
@ -4,11 +4,13 @@ import android.os.Bundle
|
|||
import com.tangem.common.routing.AppRoute.ManageTokens.Source
|
||||
import com.tangem.common.routing.bundle.RouteBundleParams
|
||||
import com.tangem.common.routing.bundle.bundle
|
||||
import com.tangem.common.routing.entity.InitScreenLaunchMode
|
||||
import com.tangem.common.routing.entity.SerializableIntent
|
||||
import com.tangem.core.decompose.navigation.Route
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.feedback.models.CardInfo
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -23,10 +25,16 @@ sealed class AppRoute(val path: String) : Route {
|
|||
data object Initial : AppRoute(path = "/initial")
|
||||
|
||||
@Serializable
|
||||
data object Home : AppRoute(path = "/home")
|
||||
data class Home(
|
||||
val launchMode: InitScreenLaunchMode = InitScreenLaunchMode.Standard,
|
||||
) : AppRoute(path = "/home")
|
||||
|
||||
@Serializable
|
||||
data class Welcome(
|
||||
@Deprecated("No longer used, will be removed in future releases")
|
||||
val launchMode: InitScreenLaunchMode = InitScreenLaunchMode.Standard,
|
||||
// we still have this param to be handled by WalletConnectLinkIntentHandler in WelcomeMiddleware
|
||||
@Deprecated("No longer used, will be removed in future releases")
|
||||
val intent: SerializableIntent? = null,
|
||||
) : AppRoute(path = "/welcome"), RouteBundleParams {
|
||||
|
||||
|
|
@ -129,6 +137,8 @@ sealed class AppRoute(val path: String) : Route {
|
|||
val initialCurrency: CryptoCurrency,
|
||||
val selectedCurrency: CryptoCurrency?,
|
||||
val source: Source,
|
||||
val showSendViaSwapNotification: Boolean,
|
||||
val analyticsCategoryName: String,
|
||||
) : AppRoute(path = "/$source/choose_managed_tokens/$userWalletId/${initialCurrency.id.value}") {
|
||||
enum class Source {
|
||||
SendViaSwap,
|
||||
|
|
@ -304,6 +314,21 @@ sealed class AppRoute(val path: String) : Route {
|
|||
@Serializable
|
||||
object AddExistingWallet : AppRoute(path = "/add_existing_wallet")
|
||||
|
||||
@Serializable
|
||||
data class WalletActivation(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/wallet_activation/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class CreateWalletBackup(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/create_wallet_backup/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class UpdateAccessCode(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/update_access_code/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class SendEntryPoint(
|
||||
val userWalletId: UserWalletId,
|
||||
|
|
@ -317,4 +342,27 @@ sealed class AppRoute(val path: String) : Route {
|
|||
val userWalletId: UserWalletId,
|
||||
val currency: CryptoCurrency,
|
||||
) : AppRoute(path = "/send_with_swap/${userWalletId.stringValue}/${currency.symbol}")
|
||||
|
||||
@Serializable
|
||||
data class CreateAccount(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/create_account/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class EditAccount(
|
||||
val account: Account,
|
||||
) : AppRoute(path = "/edit_account/${account.accountId.value}")
|
||||
|
||||
@Serializable
|
||||
data class AccountDetails(
|
||||
val account: Account,
|
||||
) : AppRoute(path = "/account_details/${account.accountId.value}")
|
||||
|
||||
@Serializable
|
||||
data class ArchivedAccountList(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/archived_account/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data object TangemPayDetails : AppRoute(path = "/tangem_pay_details")
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.common.routing.entity
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed class InitScreenLaunchMode {
|
||||
|
||||
@Serializable
|
||||
data object Standard : InitScreenLaunchMode()
|
||||
|
||||
@Serializable
|
||||
data object WithCardScan : InitScreenLaunchMode()
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ data class SerializableIntent(
|
|||
val packageValue: String?,
|
||||
val component: String?,
|
||||
val flags: Int,
|
||||
// CAUTION: works wrong with SerializableBundle constructor(bundle: Bundle), need to be removed
|
||||
val extras: SerializableBundle?,
|
||||
) {
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO
|
|||
import com.tangem.datasource.api.stakekit.models.response.model.NetworkTypeDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.TokenDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import com.tangem.domain.models.staking.StakingID
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.datasource.api.stakekit.models.response.model.AddressArgumentD
|
|||
import com.tangem.datasource.api.stakekit.models.response.model.NetworkTypeDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.TokenDTO
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import com.tangem.domain.models.staking.StakingID
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import com.tangem.blockchainsdk.utils.toNetworkId
|
|||
import com.tangem.common.test.domain.card.MockScanResponseFactory
|
||||
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.domain.card.DerivationStyleProvider
|
||||
import com.tangem.domain.card.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import com.tangem.domain.card.configs.GenericCardConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
|
@ -25,6 +25,7 @@ class MockCryptoCurrencyFactory(private val userWallet: UserWallet.Cold = defaul
|
|||
val cardano by lazy { createCoin(blockchain = Blockchain.Cardano) }
|
||||
val chia by lazy { createCoin(Blockchain.Chia) }
|
||||
val ethereum by lazy { createCoin(Blockchain.Ethereum) }
|
||||
val stellar by lazy { createCoin(Blockchain.Stellar) }
|
||||
|
||||
val chiaAndEthereum by lazy {
|
||||
listOf(
|
||||
|
|
@ -64,6 +65,10 @@ class MockCryptoCurrencyFactory(private val userWallet: UserWallet.Cold = defaul
|
|||
hasFiatFeeRate = blockchain.feePaidCurrency() !is FeePaidCurrency.FeeResource,
|
||||
canHandleTokens = false,
|
||||
transactionExtrasType = Network.TransactionExtrasType.NONE,
|
||||
nameResolvingType = when (blockchain) {
|
||||
Blockchain.Ethereum, Blockchain.EthereumTestnet -> Network.NameResolvingType.ENS
|
||||
else -> Network.NameResolvingType.NONE
|
||||
},
|
||||
)
|
||||
|
||||
return factory.createCoin(network = network)
|
||||
|
|
@ -94,6 +99,7 @@ class MockCryptoCurrencyFactory(private val userWallet: UserWallet.Cold = defaul
|
|||
hasFiatFeeRate = true,
|
||||
canHandleTokens = true,
|
||||
transactionExtrasType = Network.TransactionExtrasType.NONE,
|
||||
nameResolvingType = Network.NameResolvingType.NONE,
|
||||
),
|
||||
name = "NEVER-MIND",
|
||||
symbol = "NEVER-MIND",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,24 @@ fun <B> assertEither(actual: Either<Throwable, B>, expected: Either<Throwable, B
|
|||
.onLeft {
|
||||
val expectedError = expected.leftOrNull() ?: error("Actual is Either.Left: $it")
|
||||
|
||||
Truth.assertThat(it::class.java).isEqualTo(expectedError::class.java)
|
||||
Truth.assertThat(it).isInstanceOf(expectedError::class.java)
|
||||
Truth.assertThat(it).hasMessageThat().isEqualTo(expectedError.message)
|
||||
}
|
||||
}
|
||||
|
||||
fun assertEitherRight(actual: Either<Throwable, Unit>) {
|
||||
actual
|
||||
.onRight { Truth.assertThat(actual).isEqualTo(Either.Right(Unit)) }
|
||||
.onLeft {
|
||||
error("Actual is Either.Left: $it")
|
||||
}
|
||||
}
|
||||
|
||||
fun <B> assertEitherLeft(actual: Either<Throwable, B>, expected: Throwable) {
|
||||
actual
|
||||
.onRight { error("Actual is Either.Right: $it") }
|
||||
.onLeft {
|
||||
Truth.assertThat(it::class.java).isEqualTo(expected::class.java)
|
||||
Truth.assertThat(it).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,6 @@ dependencies {
|
|||
|
||||
/** Compose */
|
||||
implementation(deps.compose.material3)
|
||||
implementation(deps.compose.material)
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.ui)
|
||||
implementation(deps.compose.ui.tooling)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
package com.tangem.common.ui.account
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.account.AccountIconPreviewData.randomAccountIcon
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon.Color
|
||||
|
||||
enum class AccountIconSize {
|
||||
Default, Large, Medium, Small, ExtraSmall
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an account icon that can either show a letter (derived from [name])
|
||||
* or a predefined vector resource (from [icon]).
|
||||
*
|
||||
* The background color is determined by the icon's [CryptoPortfolioIconUM.color],
|
||||
* and the icon size, text style, and box modifier are adapted based on the given [size].
|
||||
*
|
||||
* @param name The text reference used to resolve and display the first letter
|
||||
* when [icon] is set to [CryptoPortfolioIcon.Icon.Letter].
|
||||
* @param icon The account icon definition, which can be a letter or a drawable resource.
|
||||
* @param size The size of the icon, defined by [AccountIconSize].
|
||||
*/
|
||||
@Composable
|
||||
fun AccountIcon(
|
||||
name: TextReference,
|
||||
icon: CryptoPortfolioIconUM,
|
||||
size: AccountIconSize,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val boxModifier = modifier.selectBoxModifier(size)
|
||||
val iconSize = Modifier.selectIconSize(size)
|
||||
val textStyle = when (size) {
|
||||
AccountIconSize.Default -> TangemTheme.typography.h3
|
||||
AccountIconSize.Large -> TangemTheme.typography.h1
|
||||
AccountIconSize.Medium -> TangemTheme.typography.subtitle1
|
||||
AccountIconSize.Small -> TangemTheme.typography.subtitle2
|
||||
AccountIconSize.ExtraSmall -> TangemTheme.typography.caption1
|
||||
}
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = boxModifier.background(icon.color.getUiColor()),
|
||||
) {
|
||||
val icon = icon.value
|
||||
val letter = name.resolveReference().firstOrNull()
|
||||
when {
|
||||
icon == CryptoPortfolioIcon.Icon.Letter -> Text(
|
||||
text = letter?.uppercase() ?: "",
|
||||
style = textStyle,
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
)
|
||||
else -> Icon(
|
||||
modifier = iconSize,
|
||||
tint = TangemTheme.colors.text.constantWhite,
|
||||
imageVector = ImageVector.vectorResource(id = icon.getResId()),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.selectIconSize(size: AccountIconSize): Modifier = when (size) {
|
||||
AccountIconSize.Default -> this.size(20.dp)
|
||||
AccountIconSize.Large -> this.size(40.dp)
|
||||
AccountIconSize.Medium -> this.size(16.dp)
|
||||
AccountIconSize.Small -> this.size(12.dp)
|
||||
AccountIconSize.ExtraSmall -> this.size(8.dp)
|
||||
}
|
||||
|
||||
private fun Modifier.selectBoxModifier(size: AccountIconSize): Modifier = when (size) {
|
||||
AccountIconSize.Default -> size(36.dp).clip(RoundedCornerShape(10.dp))
|
||||
AccountIconSize.Large -> size(88.dp).clip(RoundedCornerShape(24.dp))
|
||||
AccountIconSize.Medium -> size(28.dp).clip(RoundedCornerShape(8.dp))
|
||||
AccountIconSize.Small -> size(20.dp).clip(RoundedCornerShape(6.dp))
|
||||
AccountIconSize.ExtraSmall -> size(14.dp).clip(RoundedCornerShape(4.dp))
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_AccountIcon() {
|
||||
TangemThemePreview {
|
||||
Sample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Sample() {
|
||||
val name = stringReference("Account Name")
|
||||
Row(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
|
||||
AccountIcon(name = name, randomAccountIcon(), size = AccountIconSize.Default)
|
||||
AccountIcon(name = name, randomAccountIcon(), size = AccountIconSize.Large)
|
||||
AccountIcon(name = name, randomAccountIcon(), size = AccountIconSize.Medium)
|
||||
AccountIcon(name = name, randomAccountIcon(), size = AccountIconSize.Small)
|
||||
AccountIcon(name = name, randomAccountIcon(), size = AccountIconSize.ExtraSmall)
|
||||
}
|
||||
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
|
||||
AccountIcon(name = name, randomAccountIcon(letter = true), size = AccountIconSize.Default)
|
||||
AccountIcon(name = name, randomAccountIcon(letter = true), size = AccountIconSize.Large)
|
||||
AccountIcon(name = name, randomAccountIcon(letter = true), size = AccountIconSize.Medium)
|
||||
AccountIcon(name = name, randomAccountIcon(letter = true), size = AccountIconSize.Small)
|
||||
AccountIcon(name = name, randomAccountIcon(letter = true), size = AccountIconSize.ExtraSmall)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AccountIconPreviewData {
|
||||
|
||||
fun randomAccountIcon(letter: Boolean = false) = CryptoPortfolioIconUM(
|
||||
value = if (letter) CryptoPortfolioIcon.Icon.Letter else CryptoPortfolioIcon.Icon.entries.random(),
|
||||
color = Color.entries.random(),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
package com.tangem.common.ui.account
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Displays a row representing an account with an icon, title, and subtitle.
|
||||
*
|
||||
* The row consists of:
|
||||
* - An [AccountIcon] on the left.
|
||||
* - A column with the [title] and [subtitle] texts, which can be displayed in normal
|
||||
* or reversed order depending on [isReverse].
|
||||
*
|
||||
* The layout uses horizontal spacing between the icon and text, and vertical spacing
|
||||
* between the title and subtitle.
|
||||
*
|
||||
* @param title The main text shown in the row, usually representing the account name.
|
||||
* @param subtitle The secondary text, typically providing additional details about the account.
|
||||
* @param icon The account icon definition, displayed using [AccountIcon].
|
||||
* @param isReverse If `true`, the [subtitle] is displayed above the [title].
|
||||
* Otherwise, the [title] is displayed above the [subtitle].
|
||||
*/
|
||||
@Composable
|
||||
fun AccountRow(
|
||||
title: TextReference,
|
||||
subtitle: TextReference,
|
||||
icon: CryptoPortfolioIconUM,
|
||||
modifier: Modifier = Modifier,
|
||||
isReverse: Boolean = false,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
AccountIcon(
|
||||
name = title,
|
||||
icon = icon,
|
||||
size = AccountIconSize.Default,
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
|
||||
) {
|
||||
if (isReverse) {
|
||||
Subtitle(subtitle)
|
||||
Title(title)
|
||||
} else {
|
||||
Title(title)
|
||||
Subtitle(subtitle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Title(title: TextReference) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Subtitle(subtitle: TextReference) {
|
||||
Text(
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption2,
|
||||
text = subtitle.resolveReference(),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
Sample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Sample() {
|
||||
val name = stringReference("Main account")
|
||||
val info = stringReference("10 tokens in 2 networks")
|
||||
val subtitle = resourceReference(R.string.account_form_name)
|
||||
fun icon(letter: Boolean = false) = AccountIconPreviewData.randomAccountIcon(letter)
|
||||
Column(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
AccountRow(title = name, subtitle = info, icon = icon())
|
||||
AccountRow(title = name, subtitle = subtitle, icon = icon(), isReverse = true)
|
||||
}
|
||||
}
|
||||
|
|
@ -47,4 +47,7 @@ fun CryptoPortfolioIcon.Icon.getResId(): Int {
|
|||
CryptoPortfolioIcon.Icon.Package -> R.drawable.ic_package_24
|
||||
CryptoPortfolioIcon.Icon.Gift -> R.drawable.ic_gift_24
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun CryptoPortfolioIcon.toUM() = CryptoPortfolioIconUM(domainModel = this)
|
||||
fun CryptoPortfolioIconUM.toDomain() = CryptoPortfolioIcon.ofCustomAccount(value = this.value, color = this.color)
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.common.ui.account
|
||||
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon.Color
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon.Icon
|
||||
|
||||
data class CryptoPortfolioIconUM(
|
||||
val value: Icon,
|
||||
val color: Color,
|
||||
) {
|
||||
constructor(domainModel: CryptoPortfolioIcon) : this(
|
||||
value = domainModel.value,
|
||||
color = domainModel.color,
|
||||
)
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import com.tangem.core.ui.extensions.wrappedList
|
|||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import java.math.BigDecimal
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto
|
|||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import java.math.BigDecimal
|
||||
|
|
|
|||
|
|
@ -9,15 +9,18 @@ import com.tangem.common.ui.amountScreen.models.AmountSegmentedButtonsConfig
|
|||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.StringsSigns.DOT
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -58,6 +61,7 @@ class AmountStateConverter(
|
|||
return AmountState.Data(
|
||||
title = value.title,
|
||||
availableBalance = resourceReference(R.string.common_crypto_fiat_format, wrappedList(crypto, fiat)),
|
||||
availableBalanceShort = stringReference(crypto),
|
||||
tokenName = stringReference(status.currency.name),
|
||||
tokenIconState = iconStateConverter.convert(status),
|
||||
amountTextField = amountFieldConverter.convert(value.value),
|
||||
|
|
@ -94,7 +98,9 @@ class AmountStateConverter(
|
|||
* @property maxEnterAmount max enter amount data
|
||||
* @property cryptoCurrencyStatus current cryptocurrency status
|
||||
* @property iconStateConverter currency icon converter
|
||||
* @property isBalanceHidden is balance hidden status
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
class AmountStateConverterV2(
|
||||
private val clickIntents: AmountScreenClickIntents,
|
||||
private val appCurrency: AppCurrency,
|
||||
|
|
@ -102,6 +108,7 @@ class AmountStateConverterV2(
|
|||
private val maxEnterAmount: EnterAmountBoundary,
|
||||
private val iconStateConverter: CryptoCurrencyToIconStateConverter,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
private val isBalanceHidden: Boolean,
|
||||
) : Converter<AmountParameters, AmountState> {
|
||||
|
||||
private val amountFieldConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
|
|
@ -117,13 +124,25 @@ class AmountStateConverterV2(
|
|||
val crypto = maxEnterAmount.amount.format { crypto(cryptoCurrencyStatus.currency) }
|
||||
val noFeeRate = cryptoCurrencyStatus.value.fiatRate.isNullOrZero()
|
||||
|
||||
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Loading) {
|
||||
return AmountState.Empty(
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
)
|
||||
}
|
||||
|
||||
return AmountState.Data(
|
||||
title = value.title,
|
||||
availableBalance = if (isRedesignEnabled) {
|
||||
resourceReference(R.string.common_balance, wrappedList(crypto))
|
||||
combinedReference(
|
||||
stringReference(crypto),
|
||||
stringReference(" $DOT "),
|
||||
stringReference(fiat),
|
||||
).orMaskWithStars(isBalanceHidden)
|
||||
} else {
|
||||
resourceReference(R.string.common_crypto_fiat_format, wrappedList(crypto, fiat))
|
||||
.orMaskWithStars(isBalanceHidden)
|
||||
},
|
||||
availableBalanceShort = stringReference(crypto).orMaskWithStars(isBalanceHidden),
|
||||
tokenName = stringReference(cryptoCurrencyStatus.currency.name),
|
||||
tokenIconState = iconStateConverter.convert(cryptoCurrencyStatus.currency),
|
||||
amountTextField = amountFieldConverter.convert(value.value),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.common.ui.amountScreen.converters
|
||||
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,13 +3,17 @@ package com.tangem.common.ui.amountScreen.converters.field
|
|||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.StringsSigns.DOT
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
/**
|
||||
|
|
@ -25,6 +29,7 @@ class AmountBoundaryUpdateTransformer(
|
|||
private val maxEnterAmount: EnterAmountBoundary,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
private val isBalanceHidden: Boolean,
|
||||
) : Transformer<AmountState> {
|
||||
|
||||
override fun transform(prevState: AmountState): AmountState {
|
||||
|
|
@ -34,13 +39,18 @@ class AmountBoundaryUpdateTransformer(
|
|||
val crypto = maxEnterAmount.amount.format { crypto(cryptoCurrencyStatus.currency) }
|
||||
|
||||
val availableBalance = if (isRedesignEnabled) {
|
||||
resourceReference(R.string.common_balance, wrappedList(crypto))
|
||||
combinedReference(
|
||||
stringReference(crypto),
|
||||
stringReference(" $DOT "),
|
||||
stringReference(fiat),
|
||||
)
|
||||
} else {
|
||||
resourceReference(R.string.common_crypto_fiat_format, wrappedList(crypto, fiat))
|
||||
}
|
||||
|
||||
return prevState.copy(
|
||||
availableBalance = availableBalance,
|
||||
availableBalance = availableBalance.orMaskWithStars(isBalanceHidden),
|
||||
availableBalanceShort = stringReference(crypto).orMaskWithStars(isBalanceHidden),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ import com.tangem.core.ui.extensions.wrappedList
|
|||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -39,7 +39,7 @@ class AmountFieldChangeTransformer(
|
|||
|
||||
val amountTextField = prevState.amountTextField
|
||||
|
||||
if (value.isEmpty()) return prevState.emptyState()
|
||||
if (value.isEmpty()) return prevState.emptyState(maxEnterAmount.fiatRate)
|
||||
val cryptoDecimals = amountTextField.cryptoAmount.decimals
|
||||
val fiatDecimals = amountTextField.fiatAmount.decimals
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ class AmountFieldChangeTransformer(
|
|||
error = when {
|
||||
isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance)
|
||||
isLessThanMinimumIfProvided -> {
|
||||
val minimumAmount = minimumTransactionAmount?.amount.format {
|
||||
val minimumAmount = minimumTransactionAmount.amount.format {
|
||||
crypto(cryptoCurrencyStatus.currency)
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ class AmountFieldChangeTransformer(
|
|||
)
|
||||
}
|
||||
|
||||
private fun AmountState.Data.emptyState(): AmountState.Data {
|
||||
private fun AmountState.Data.emptyState(fiatRate: BigDecimal?): AmountState.Data {
|
||||
return copy(
|
||||
isPrimaryButtonEnabled = false,
|
||||
reduceAmountBy = BigDecimal.ZERO,
|
||||
|
|
@ -104,7 +104,7 @@ class AmountFieldChangeTransformer(
|
|||
value = "",
|
||||
fiatValue = "",
|
||||
cryptoAmount = amountTextField.cryptoAmount.copy(value = BigDecimal.ZERO),
|
||||
fiatAmount = amountTextField.fiatAmount.copy(value = BigDecimal.ZERO),
|
||||
fiatAmount = amountTextField.fiatAmount.copy(value = if (fiatRate != null) BigDecimal.ZERO else null),
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.None,
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import com.tangem.common.ui.amountScreen.models.AmountFieldModel
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.Amount
|
||||
import com.tangem.domain.tokens.model.AmountType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.convertToAmount
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
|
@ -100,8 +100,8 @@ class AmountFieldConverterV2(
|
|||
val cryptoAmount = cryptoDecimal.convertToAmount(cryptoCurrencyStatus.currency)
|
||||
val fiatRate = cryptoCurrencyStatus.value.fiatRate
|
||||
val (fiatValue, fiatDecimal) = when {
|
||||
value.isEmpty() -> "" to BigDecimal.ZERO
|
||||
fiatRate.isNullOrZero() -> "" to null
|
||||
value.isEmpty() -> "" to BigDecimal.ZERO
|
||||
else -> {
|
||||
val fiatDecimal = fiatRate?.multiply(cryptoDecimal)
|
||||
val fiatValue = fiatDecimal?.parseBigDecimal(FIAT_DECIMALS).orEmpty()
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import com.tangem.core.ui.extensions.wrappedList
|
|||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import java.math.BigDecimal
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ sealed class AmountState {
|
|||
/**
|
||||
* @param isPrimaryButtonEnabled indicates if next state button enabled
|
||||
* @param title title
|
||||
* @param availableBalance user crypto currency balance
|
||||
* @param availableBalance user crypto currency balance with fiat balance
|
||||
* @param availableBalanceShort user crypto currency balance without fiat balance
|
||||
* @param tokenIconState crypto currency icon state
|
||||
* @param segmentedButtonConfig currency switcher config
|
||||
* @param selectedButton selected currency index
|
||||
|
|
@ -33,6 +34,7 @@ sealed class AmountState {
|
|||
override val isRedesignEnabled: Boolean,
|
||||
val title: TextReference,
|
||||
val availableBalance: TextReference,
|
||||
val availableBalanceShort: TextReference,
|
||||
val tokenName: TextReference,
|
||||
val tokenIconState: CurrencyIconState,
|
||||
val segmentedButtonConfig: PersistentList<AmountSegmentedButtonsConfig>,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ object AmountStatePreviewData {
|
|||
val amountState = AmountState.Data(
|
||||
isPrimaryButtonEnabled = false,
|
||||
title = stringReference("Family Wallet"),
|
||||
availableBalance = stringReference("2 130,88 USDT (2 129,92 \$)"),
|
||||
availableBalance = stringReference("2 130,88 USDT • 2 129,92 \$)"),
|
||||
availableBalanceShort = stringReference("2 130,88 USDT"),
|
||||
tokenIconState = CurrencyIconState.Loading,
|
||||
segmentedButtonConfig = persistentListOf(
|
||||
AmountSegmentedButtonsConfig(
|
||||
|
|
@ -92,6 +93,13 @@ object AmountStatePreviewData {
|
|||
amountTextField = amountWithValueState.amountTextField.copy(isFiatValue = false),
|
||||
)
|
||||
|
||||
val amountStateV2WithoutRates = amountState.copy(
|
||||
amountTextField = amountState.amountTextField.copy(
|
||||
fiatAmount = amountState.amountTextField.fiatAmount.copy(
|
||||
value = null,
|
||||
),
|
||||
),
|
||||
)
|
||||
val amountErrorState = amountWithValueState.copy(
|
||||
amountTextField = amountWithValueState.amountTextField.copy(
|
||||
isError = true,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -25,6 +26,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
|||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.StakingSendDetailsScreenTestTags
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Composable
|
||||
|
|
@ -63,7 +65,8 @@ fun AmountBlock(amountState: AmountState, isClickDisabled: Boolean, isEditingDis
|
|||
maxLines = 1,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = TangemTheme.dimens.spacing24),
|
||||
.padding(top = TangemTheme.dimens.spacing24)
|
||||
.testTag(StakingSendDetailsScreenTestTags.PRIMARY_AMOUNT),
|
||||
)
|
||||
Text(
|
||||
text = secondAmount,
|
||||
|
|
@ -72,7 +75,8 @@ fun AmountBlock(amountState: AmountState, isClickDisabled: Boolean, isEditingDis
|
|||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = TangemTheme.dimens.spacing8),
|
||||
.padding(top = TangemTheme.dimens.spacing8)
|
||||
.testTag(StakingSendDetailsScreenTestTags.SECONDARY_AMOUNT),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.preview.AmountStatePreviewData
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
|
|
@ -24,6 +25,7 @@ import com.tangem.core.ui.extensions.resolveReference
|
|||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.uncapped
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
|
|
@ -43,7 +45,7 @@ fun AmountBlockV2(
|
|||
crypto(
|
||||
symbol = "",
|
||||
decimals = amount.cryptoAmount.decimals,
|
||||
)
|
||||
).uncapped()
|
||||
}.orEmpty()
|
||||
|
||||
val fiatAmount = amount.fiatAmount.value.format {
|
||||
|
|
@ -62,7 +64,7 @@ fun AmountBlockV2(
|
|||
|
||||
AmountBlockV2(
|
||||
title = amountState.title,
|
||||
balance = amountState.availableBalance,
|
||||
balance = amountState.availableBalanceShort,
|
||||
currencyTitle = currencyTitle,
|
||||
currencyIconState = amountState.tokenIconState,
|
||||
firstAmount = firstAmount,
|
||||
|
|
@ -102,7 +104,7 @@ private fun AmountBlockV2(
|
|||
Row {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
SpacerWMax()
|
||||
|
|
@ -121,18 +123,20 @@ private fun AmountBlockV2(
|
|||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
ResizableText(
|
||||
text = firstAmount,
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
ResizableText(
|
||||
text = secondAmount,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
)
|
||||
extraContent()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.amountScreen.AmountScreenClickIntents
|
||||
import com.tangem.common.ui.amountScreen.models.AmountSegmentedButtonsConfig
|
||||
|
|
@ -22,6 +23,7 @@ import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.StakingSendScreenTestTags
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
||||
private const val AMOUNT_BUTTONS_KEY = "amountButtonsKey"
|
||||
|
|
@ -77,7 +79,8 @@ internal fun LazyListScope.buttons(
|
|||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing10,
|
||||
horizontal = TangemTheme.dimens.spacing34,
|
||||
),
|
||||
)
|
||||
.testTag(StakingSendScreenTestTags.MAX_BUTTON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -90,7 +93,8 @@ private fun AmountCurrencyButton(button: AmountSegmentedButtonsConfig, isSegment
|
|||
.fillMaxSize()
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing10,
|
||||
),
|
||||
)
|
||||
.testTag(StakingSendScreenTestTags.CURRENCY_BUTTON),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
|
|
@ -102,13 +106,13 @@ private fun AmountCurrencyButton(button: AmountSegmentedButtonsConfig, isSegment
|
|||
url = button.iconUrl,
|
||||
size = TangemTheme.dimens.size18,
|
||||
isGrayscale = !isSegmentedButtonsEnabled,
|
||||
modifier = iconModifier,
|
||||
modifier = iconModifier.testTag(StakingSendScreenTestTags.FIAT_ICON),
|
||||
)
|
||||
} else if (button.iconState != null) {
|
||||
CurrencyIcon(
|
||||
state = button.iconState,
|
||||
shouldDisplayNetwork = false,
|
||||
modifier = iconModifier,
|
||||
modifier = iconModifier.testTag(StakingSendScreenTestTags.CURRENCY_ICON),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import androidx.compose.ui.Alignment.Companion.TopCenter
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import com.tangem.common.ui.amountScreen.models.AmountFieldModel
|
||||
|
|
@ -28,6 +29,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto
|
|||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.StakingSendScreenTestTags
|
||||
import com.tangem.core.ui.utils.rememberDecimalFormat
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
|
|
@ -116,7 +118,8 @@ private fun AmountSecondary(amountField: AmountFieldModel, appCurrencyCode: Stri
|
|||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.align(TopCenter)
|
||||
.padding(bottom = TangemTheme.dimens.spacing32),
|
||||
.padding(bottom = TangemTheme.dimens.spacing32)
|
||||
.testTag(StakingSendScreenTestTags.SECONDARY_AMOUNT),
|
||||
)
|
||||
AmountFieldError(
|
||||
isError = amountField.isError,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.R
|
||||
|
|
@ -29,6 +30,7 @@ import com.tangem.core.ui.extensions.orMaskWithStars
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.StakingSendScreenTestTags
|
||||
|
||||
private const val AMOUNT_FIELD_KEY = "amountFieldKey"
|
||||
|
||||
|
|
@ -52,7 +54,8 @@ internal fun LazyListScope.amountField(
|
|||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing14),
|
||||
.padding(top = TangemTheme.dimens.spacing14)
|
||||
.testTag(StakingSendScreenTestTags.AMOUNT_CONTAINER_TITLE),
|
||||
)
|
||||
|
||||
val balance = amountState.availableBalance.orMaskWithStars(isBalanceHidden).resolveReference()
|
||||
|
|
@ -66,7 +69,8 @@ internal fun LazyListScope.amountField(
|
|||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing2),
|
||||
.padding(top = TangemTheme.dimens.spacing2)
|
||||
.testTag(StakingSendScreenTestTags.AMOUNT_CONTAINER_TEXT),
|
||||
)
|
||||
}
|
||||
CurrencyIcon(
|
||||
|
|
@ -113,7 +117,7 @@ internal fun LazyListScope.amountFieldV2(
|
|||
} else {
|
||||
Text(
|
||||
text = amountState.title.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
|
|
@ -164,7 +168,7 @@ private fun AmountInfo(amountUM: AmountState, onMaxAmountClick: () -> Unit, modi
|
|||
modifier = Modifier
|
||||
.padding(end = 16.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(TangemTheme.colors.background.secondary)
|
||||
.background(TangemTheme.colors.button.secondary)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(),
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Alignment.Companion.BottomCenter
|
||||
import androidx.compose.ui.Alignment.Companion.TopCenter
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
|
|
@ -127,7 +128,7 @@ private fun AmountSecondary(amountUM: AmountState, onCurrencyChange: (Boolean) -
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.animateContentSize()
|
||||
.padding(top = 8.dp),
|
||||
.padding(top = 4.dp),
|
||||
) {
|
||||
if (amountUM is AmountState.Empty) {
|
||||
TextShimmer(
|
||||
|
|
@ -148,7 +149,7 @@ private fun AmountSecondary(amountUM: AmountState, onCurrencyChange: (Boolean) -
|
|||
error = amountUM.amountTextField.error,
|
||||
modifier = Modifier
|
||||
.align(BottomCenter)
|
||||
.padding(top = 20.dp),
|
||||
.padding(top = 24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -156,17 +157,21 @@ private fun AmountSecondary(amountUM: AmountState, onCurrencyChange: (Boolean) -
|
|||
|
||||
@Composable
|
||||
private fun BoxScope.AmountFieldCurrencyInfo(amountUM: AmountState.Data, onCurrencyChange: (Boolean) -> Unit) {
|
||||
val isFiatAvailable = amountUM.amountTextField.fiatAmount.value != null
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier
|
||||
.align(TopCenter)
|
||||
.padding(bottom = 20.dp)
|
||||
.padding(bottom = 16.dp)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = { onCurrencyChange(!amountUM.amountTextField.isFiatValue) },
|
||||
),
|
||||
)
|
||||
.alpha(if (isFiatAvailable) 1f else 0f)
|
||||
.padding(4.dp),
|
||||
) {
|
||||
val iconRotateState by animateFloatAsState(
|
||||
targetValue = if (amountUM.amountTextField.isFiatValue) ROTATED_DEGREE else INITIAL_DEGREE,
|
||||
|
|
@ -324,6 +329,8 @@ private class AmountFieldV2PreviewProvider : PreviewParameterProvider<AmountStat
|
|||
get() = sequenceOf(
|
||||
AmountStatePreviewData.emptyState,
|
||||
AmountStatePreviewData.amountState,
|
||||
AmountStatePreviewData.amountErrorState,
|
||||
AmountStatePreviewData.amountStateV2WithoutRates,
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -2,9 +2,11 @@ package com.tangem.common.ui.amountScreen.utils
|
|||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter.EMPTY_BALANCE_SIGN
|
||||
import com.tangem.core.ui.format.bigdecimal.approximateAmount
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import java.math.BigDecimal
|
||||
|
||||
fun getFiatReference(value: BigDecimal?, rate: BigDecimal?, appCurrency: AppCurrency): TextReference? {
|
||||
|
|
@ -19,12 +21,19 @@ fun getFiatString(
|
|||
appCurrency: AppCurrency,
|
||||
approximate: Boolean = false,
|
||||
): String {
|
||||
if (value == null || rate == null) return EMPTY_BALANCE_SIGN
|
||||
if (value == null || rate == null) return DASH_SIGN
|
||||
val feeValue = value.multiply(rate)
|
||||
return BigDecimalFormatter.formatFiatAmount(
|
||||
fiatAmount = feeValue,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
withApproximateSign = approximate,
|
||||
)
|
||||
return feeValue.format {
|
||||
if (approximate) {
|
||||
fiat(
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
).approximateAmount()
|
||||
} else {
|
||||
fiat(
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -43,9 +43,9 @@ fun GiveTxPermissionBottomSheet(config: TangemBottomSheetConfig) {
|
|||
config = config,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
titleText = resourceReference(R.string.give_permission_title),
|
||||
titleAction = TopAppBarButtonUM(
|
||||
titleAction = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_information_24,
|
||||
onIconClicked = { isPermissionAlertShow = true },
|
||||
onClicked = { isPermissionAlertShow = true },
|
||||
),
|
||||
content = { content: GiveTxPermissionBottomSheetConfig ->
|
||||
GiveTxPermissionBottomSheetContent(content = content)
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ fun SendingText(footerText: TextReference, modifier: Modifier = Modifier) {
|
|||
text = footerText.resolveAnnotatedReference(),
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
.padding(16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,9 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -22,18 +25,18 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import com.tangem.common.ui.navigationButtons.preview.NavigationButtonsPreview
|
||||
import com.tangem.core.ui.components.Keyboard
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconStart
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButton
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
|
||||
import com.tangem.core.ui.components.buttons.common.contentColor
|
||||
import com.tangem.core.ui.components.keyboardAsState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.isNullOrEmpty
|
||||
import com.tangem.core.ui.extensions.rememberHapticFeedback
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import com.tangem.core.ui.utils.singleEvent
|
||||
import com.tangem.core.ui.test.StakingSendScreenTestTags
|
||||
|
||||
@Composable
|
||||
fun NavigationButtonsBlock(
|
||||
|
|
@ -47,7 +50,7 @@ fun NavigationButtonsBlock(
|
|||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
InfoText(footerText)
|
||||
ExtraButtons(state?.extraButtons, state?.txUrl)
|
||||
DoneButtons(state?.extraButtons)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
|
|
@ -58,9 +61,33 @@ fun NavigationButtonsBlock(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavigationButtonsBlockV2(
|
||||
navigationUM: NavigationUM,
|
||||
modifier: Modifier = Modifier,
|
||||
footerText: TextReference? = null,
|
||||
) {
|
||||
val navigationUM = navigationUM as? NavigationUM.Content
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
InfoText(footerText)
|
||||
DoneButtons(navigationUM?.secondaryPairButtonsUM)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
PreviousButton(navigationUM?.prevButton)
|
||||
NavigationPrimaryButton(navigationUM?.primaryButton, modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavigationPrimaryButton(primaryButton: NavigationButton?, modifier: Modifier = Modifier) {
|
||||
val wrappedButton by rememberNavigationButton(primaryButton)
|
||||
val hapticFeedback = LocalHapticFeedback.current
|
||||
AnimatedContent(
|
||||
targetState = wrappedButton,
|
||||
transitionSpec = { navigationButtonsTransition() },
|
||||
|
|
@ -83,7 +110,12 @@ fun NavigationPrimaryButton(primaryButton: NavigationButton?, modifier: Modifier
|
|||
TangemButton(
|
||||
text = button.textReference.resolveReference(),
|
||||
enabled = button.isEnabled,
|
||||
onClick = button.onClick,
|
||||
onClick = {
|
||||
if (button.isHapticClick) {
|
||||
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
}
|
||||
button.onClick()
|
||||
},
|
||||
showProgress = button.showProgress,
|
||||
colors = color,
|
||||
textStyle = TangemTheme.typography.subtitle1,
|
||||
|
|
@ -116,40 +148,47 @@ private fun PreviousButton(prevButton: NavigationButton?) {
|
|||
.clip(RoundedCornerShape(TangemTheme.dimens.radius16))
|
||||
.background(TangemTheme.colors.button.secondary)
|
||||
.clickable(onClick = button.onClick)
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
.padding(TangemTheme.dimens.spacing12)
|
||||
.testTag(StakingSendScreenTestTags.PREVIOUS_BUTTON),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExtraButtons(extraButtons: ImmutableList<NavigationButton>?, txUrl: String?) {
|
||||
fun DoneButtons(pairButtons: Pair<NavigationButton, NavigationButton>?, modifier: Modifier = Modifier) {
|
||||
AnimatedVisibility(
|
||||
visible = !txUrl.isNullOrBlank() && extraButtons != null,
|
||||
visible = pairButtons != null,
|
||||
enter = slideInVertically(initialOffsetY = { it / 2 }).plus(fadeIn()),
|
||||
exit = slideOutVertically(targetOffsetY = { it / 2 }).plus(fadeOut()),
|
||||
label = "Animate show sent state buttons",
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
val buttons = remember(this) { requireNotNull(extraButtons) }
|
||||
val (leftButton, rightButton) = remember(this) { requireNotNull(pairButtons) }
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
buttons.forEach { button ->
|
||||
val icon = button.iconRes?.let { TangemButtonIconPosition.Start(iconResId = it) }
|
||||
?: TangemButtonIconPosition.None
|
||||
TangemButton(
|
||||
text = button.textReference.resolveReference(),
|
||||
icon = icon,
|
||||
textStyle = TangemTheme.typography.subtitle1,
|
||||
onClick = rememberHapticFeedback(state = button, onAction = button.onClick),
|
||||
modifier = Modifier.weight(1f),
|
||||
enabled = button.isEnabled,
|
||||
showProgress = false,
|
||||
colors = TangemButtonsDefaults.secondaryButtonColors,
|
||||
)
|
||||
}
|
||||
SecondaryButtonIconStart(
|
||||
text = leftButton.textReference.resolveReference(),
|
||||
iconResId = requireNotNull(leftButton.iconRes),
|
||||
onClick = {
|
||||
singleEvent {
|
||||
leftButton.onClick()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
SecondaryButtonIconStart(
|
||||
text = rightButton.textReference.resolveReference(),
|
||||
iconResId = requireNotNull(rightButton.iconRes),
|
||||
onClick = {
|
||||
singleEvent {
|
||||
rightButton.onClick()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.common.ui.navigationButtons
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
sealed class NavigationButtonsState {
|
||||
data object Empty : NavigationButtonsState()
|
||||
|
|
@ -10,7 +9,7 @@ sealed class NavigationButtonsState {
|
|||
data class Data(
|
||||
val primaryButton: NavigationButton?,
|
||||
val prevButton: NavigationButton?,
|
||||
val extraButtons: ImmutableList<NavigationButton>,
|
||||
val extraButtons: Pair<NavigationButton, NavigationButton>?,
|
||||
val txUrl: String? = null,
|
||||
val onTextClick: (String) -> Unit,
|
||||
) : NavigationButtonsState()
|
||||
|
|
|
|||
|
|
@ -5,29 +5,25 @@ import com.tangem.common.ui.navigationButtons.NavigationButton
|
|||
import com.tangem.common.ui.navigationButtons.NavigationButtonsState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal object NavigationButtonsPreview {
|
||||
|
||||
private val extraButtons = persistentListOf(
|
||||
NavigationButton(
|
||||
textReference = resourceReference(R.string.common_explore),
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
isSecondary = true,
|
||||
isIconVisible = true,
|
||||
showProgress = false,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
NavigationButton(
|
||||
textReference = resourceReference(R.string.common_share),
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
isSecondary = true,
|
||||
isIconVisible = true,
|
||||
showProgress = false,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
private val extraButtons = NavigationButton(
|
||||
textReference = resourceReference(R.string.common_explore),
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
isSecondary = true,
|
||||
isIconVisible = true,
|
||||
showProgress = false,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
) to NavigationButton(
|
||||
textReference = resourceReference(R.string.common_share),
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
isSecondary = true,
|
||||
isIconVisible = true,
|
||||
showProgress = false,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
private val prev = NavigationButton(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.common.ui.notifications
|
||||
|
||||
/**
|
||||
* NotificationId represents unique identifiers for notifications in the app.
|
||||
*
|
||||
* These ids can be used with [ShouldShowNotificationUseCase] and [SetShouldShowNotificationUseCase]
|
||||
* to check or update the visibility state of notifications.
|
||||
*/
|
||||
enum class NotificationId(val key: String) {
|
||||
SendViaSwapTokenSelectorNotification("SendViaSwapTokenSelectorNotificationKey"),
|
||||
}
|
||||
|
|
@ -273,6 +273,7 @@ sealed class NotificationUM(val config: NotificationConfig) {
|
|||
iconResId: Int = R.drawable.ic_alert_circle_24,
|
||||
buttonsState: NotificationConfig.ButtonsState? = null,
|
||||
onCloseClick: (() -> Unit)? = null,
|
||||
iconTint: NotificationConfig.IconTint = NotificationConfig.IconTint.Unspecified,
|
||||
) : NotificationUM(
|
||||
config = NotificationConfig(
|
||||
title = title,
|
||||
|
|
@ -280,6 +281,7 @@ sealed class NotificationUM(val config: NotificationConfig) {
|
|||
iconResId = iconResId,
|
||||
buttonsState = buttonsState,
|
||||
onCloseClick = onCloseClick,
|
||||
iconTint = iconTint,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -373,5 +375,15 @@ sealed class NotificationUM(val config: NotificationConfig) {
|
|||
formatArgs = wrappedList(rentInfo.exemptionAmount),
|
||||
),
|
||||
)
|
||||
|
||||
data class RentExemptionDestination(
|
||||
private val rentExemptionAmount: BigDecimal,
|
||||
) : Error(
|
||||
title = TextReference.Res(R.string.send_notification_invalid_amount_title),
|
||||
subtitle = TextReference.Res(
|
||||
id = R.string.send_notification_invalid_amount_rent_destination,
|
||||
formatArgs = wrappedList(rentExemptionAmount),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ import com.tangem.core.ui.format.bigdecimal.uncapped
|
|||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
|
|
@ -354,7 +354,11 @@ object NotificationsFactory {
|
|||
onReduceClick = onReduceClick,
|
||||
)
|
||||
is BlockchainSdkError.DestinationTagRequired -> addRequireDestinationTagErrorNotification()
|
||||
null -> minAdaValue?.let {
|
||||
is BlockchainSdkError.Solana.DestinationRentExemption -> addRentExemptionDestinationNotification(
|
||||
rentExemptionAmount = validationError.rentAmount,
|
||||
)
|
||||
null,
|
||||
-> minAdaValue?.let {
|
||||
add(
|
||||
NotificationUM.Cardano.MinAdaValueCharged(
|
||||
tokenName = cryptoCurrency.name,
|
||||
|
|
@ -439,6 +443,14 @@ object NotificationsFactory {
|
|||
add(NotificationUM.Solana.RentInfo(rentWarning))
|
||||
}
|
||||
|
||||
fun MutableList<NotificationUM>.addRentExemptionDestinationNotification(rentExemptionAmount: BigDecimal) {
|
||||
add(
|
||||
NotificationUM.Solana.RentExemptionDestination(
|
||||
rentExemptionAmount = rentExemptionAmount,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun MutableList<NotificationUM>.addHighFeeWarningNotification(
|
||||
enteredAmountValue: BigDecimal,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -29,6 +30,7 @@ import com.tangem.core.ui.res.LocalWindowSize
|
|||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.SwapStoriesScreenTestTags
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
private val SubtitleColor = Color(0xFFB0B0B0)
|
||||
|
|
@ -46,7 +48,8 @@ fun SwapStoriesScreen(config: SwapStoriesUM) {
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(TangemColorPalette.Black),
|
||||
.background(TangemColorPalette.Black)
|
||||
.testTag(SwapStoriesScreenTestTags.SCREEN_CONTAINER),
|
||||
) {
|
||||
SubcomposeAsyncImage(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import com.tangem.core.ui.format.bigdecimal.format
|
|||
import com.tangem.core.ui.format.bigdecimal.percent
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.isZero
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package com.tangem.common.ui.userwallet
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.CardColors
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -22,6 +24,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.common.ui.R
|
||||
|
|
@ -30,6 +33,9 @@ import com.tangem.core.ui.components.RectangleShimmer
|
|||
import com.tangem.core.ui.components.TextShimmer
|
||||
import com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.components.label.Label
|
||||
import com.tangem.core.ui.components.label.entity.LabelStyle
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.components.text.applyBladeBrush
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -67,8 +73,10 @@ fun UserWalletItem(
|
|||
balance = state.balance,
|
||||
)
|
||||
|
||||
state.label?.let { Label(it) }
|
||||
|
||||
when (state.endIcon) {
|
||||
UserWalletItemUM.EndIcon.None -> {}
|
||||
UserWalletItemUM.EndIcon.None -> Unit
|
||||
UserWalletItemUM.EndIcon.Arrow -> {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_right_24),
|
||||
|
|
@ -179,6 +187,19 @@ fun CardImage(imageState: UserWalletItemUM.ImageState, modifier: Modifier = Modi
|
|||
radius = TangemTheme.dimens.size2,
|
||||
)
|
||||
}
|
||||
is UserWalletItemUM.ImageState.MobileWallet -> {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.field.focused,
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
)
|
||||
.padding(6.dp),
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_mobile_wallet_icon_24),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
is UserWalletItemUM.ImageState.Image -> {
|
||||
val verifiedArtwork = imageState.artwork.verifiedArtwork
|
||||
if (verifiedArtwork != null) {
|
||||
|
|
@ -257,6 +278,18 @@ private fun Preview_UserWalletItem(
|
|||
private class UserWalletItemUMPreviewProvider : PreviewParameterProvider<UserWalletItemUM> {
|
||||
override val values: Sequence<UserWalletItemUM>
|
||||
get() = sequenceOf(
|
||||
UserWalletItemUM(
|
||||
id = UserWalletId("user_wallet_0".encodeToByteArray()),
|
||||
name = stringReference("Mobile Wallet"),
|
||||
information = getInformation(cardCount = 1),
|
||||
balance = UserWalletItemUM.Balance.Locked,
|
||||
label = LabelUM(
|
||||
text = resourceReference(R.string.hw_backup_no_backup),
|
||||
style = LabelStyle.WARNING,
|
||||
),
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
UserWalletItemUM(
|
||||
id = UserWalletId("user_wallet_1".encodeToByteArray()),
|
||||
name = stringReference("My Wallet"),
|
||||
|
|
@ -347,6 +380,18 @@ private class UserWalletItemUMPreviewProvider : PreviewParameterProvider<UserWal
|
|||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
UserWalletItemUM(
|
||||
id = UserWalletId("user_wallet_3".encodeToByteArray()),
|
||||
name = stringReference("Multi Card"),
|
||||
information = UserWalletItemUM.Information.Failed,
|
||||
balance = UserWalletItemUM.Balance.Loaded(
|
||||
value = "1.2345 BTC",
|
||||
isFlickering = false,
|
||||
),
|
||||
imageState = UserWalletItemUM.ImageState.MobileWallet,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
private fun getInformation(cardCount: Int): UserWalletItemUM.Information.Loaded {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ package com.tangem.common.ui.userwallet.converter
|
|||
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.components.label.entity.LabelStyle
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
|
|
@ -32,6 +35,7 @@ class UserWalletItemUMConverter(
|
|||
private val appCurrency: AppCurrency? = null,
|
||||
private val balance: TotalFiatBalance? = null,
|
||||
private val isBalanceHidden: Boolean = false,
|
||||
private val authMode: Boolean = false,
|
||||
private val endIcon: UserWalletItemUM.EndIcon = UserWalletItemUM.EndIcon.None,
|
||||
private val artwork: ArtworkModel? = null,
|
||||
) : Converter<UserWallet, UserWalletItemUM> {
|
||||
|
|
@ -45,16 +49,38 @@ class UserWalletItemUMConverter(
|
|||
name = stringReference(name),
|
||||
information = getInfo(userWallet = this),
|
||||
balance = getBalanceInfo(userWallet = this),
|
||||
isEnabled = !isLocked,
|
||||
isEnabled = isEnabled(userWallet = this),
|
||||
endIcon = endIcon,
|
||||
onClick = { onClick(value.walletId) },
|
||||
imageState = artwork?.let {
|
||||
UserWalletItemUM.ImageState.Image(artworkUMConverter.convert(it))
|
||||
} ?: UserWalletItemUM.ImageState.Loading,
|
||||
imageState = getImageState(userWallet = value),
|
||||
label = getLabelOrNull(userWallet = this),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isEnabled(userWallet: UserWallet): Boolean {
|
||||
return authMode || userWallet.isLocked.not()
|
||||
}
|
||||
|
||||
private fun getLabelOrNull(userWallet: UserWallet): LabelUM? {
|
||||
return if (authMode.not() && userWallet is UserWallet.Hot && !userWallet.backedUp) {
|
||||
LabelUM(
|
||||
text = resourceReference(R.string.hw_backup_no_backup),
|
||||
style = LabelStyle.WARNING,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getImageState(userWallet: UserWallet): UserWalletItemUM.ImageState {
|
||||
return when {
|
||||
userWallet is UserWallet.Hot -> UserWalletItemUM.ImageState.MobileWallet
|
||||
artwork != null -> UserWalletItemUM.ImageState.Image(artworkUMConverter.convert(artwork))
|
||||
else -> UserWalletItemUM.ImageState.Loading
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInfo(userWallet: UserWallet): UserWalletItemUM.Information.Loaded {
|
||||
val text = when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.common.ui.userwallet.state
|
||||
|
||||
import com.tangem.core.ui.components.artwork.ArtworkUM
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import javax.annotation.concurrent.Immutable
|
||||
|
|
@ -15,6 +16,7 @@ data class UserWalletItemUM(
|
|||
val isEnabled: Boolean,
|
||||
val endIcon: EndIcon = EndIcon.None,
|
||||
val onClick: () -> Unit,
|
||||
val label: LabelUM? = null,
|
||||
) {
|
||||
enum class EndIcon {
|
||||
None,
|
||||
|
|
@ -54,6 +56,8 @@ data class UserWalletItemUM(
|
|||
|
||||
data object Loading : ImageState()
|
||||
|
||||
data object MobileWallet : ImageState()
|
||||
|
||||
data class Image(
|
||||
val artwork: ArtworkUM,
|
||||
) : ImageState()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue