diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 9eaedddd9b..fad890b2a0 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 9eaedddd9bfe5a1f216887d31eda0fabeba1c11d +Subproject commit fad890b2a0b552be60d124949ca0a3a4d672dac1 diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokenItemState.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokenItemState.kt index df128ac9b4..790a83fb5f 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokenItemState.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokenItemState.kt @@ -1,12 +1,16 @@ package com.tangem.tap.features.tokens.impl.presentation.states +import androidx.compose.runtime.Immutable import kotlinx.collections.immutable.ImmutableList /** - * Network item state + * Token item state. + * All subclasses is stable, but @Immutable annotation is required to use this sealed class like as + * field of TokensListStateHolder. * [REDACTED_AUTHOR] */ +@Immutable sealed interface TokenItemState { /** Token id */ diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListStateHolder.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListStateHolder.kt index e083f7991b..101cdc8f52 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListStateHolder.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListStateHolder.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.tokens.impl.presentation.states +import androidx.compose.runtime.Immutable import androidx.paging.LoadState import androidx.paging.PagingData import kotlinx.coroutines.flow.Flow @@ -9,6 +10,7 @@ import kotlinx.coroutines.flow.Flow * [REDACTED_AUTHOR] */ +@Immutable internal sealed interface TokensListStateHolder { /** Toolbar state */ diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListToolbarState.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListToolbarState.kt index b4104f692b..a269659991 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListToolbarState.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListToolbarState.kt @@ -1,6 +1,13 @@ package com.tangem.tap.features.tokens.impl.presentation.states -/** Toolbar state */ +import androidx.compose.runtime.Immutable + +/** + * Toolbar state. + * All subclasses is stable, but @Immutable annotation is required to use this sealed class like as + * field of TokensListStateHolder. + */ +@Immutable sealed interface TokensListToolbarState { /** Callback to be invoked when BackButton is being clicked */ diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt index 67e10ca9a6..e82c2f9547 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt @@ -22,14 +22,22 @@ object MultipleAddressUiHelper { return when (id) { R.id.chip_default -> { when (blockchain) { - Blockchain.Bitcoin, Blockchain.BitcoinTestnet, Blockchain.Litecoin -> BitcoinAddressType.Segwit + Blockchain.Bitcoin, + Blockchain.BitcoinTestnet, + Blockchain.Litecoin, + Blockchain.BitcoinCash, + -> BitcoinAddressType.Segwit Blockchain.CardanoShelley -> CardanoAddressType.Shelley else -> null } } R.id.chip_legacy -> { when (blockchain) { - Blockchain.Bitcoin, Blockchain.BitcoinTestnet, Blockchain.Litecoin -> BitcoinAddressType.Legacy + Blockchain.Bitcoin, + Blockchain.BitcoinTestnet, + Blockchain.Litecoin, + Blockchain.BitcoinCash, + -> BitcoinAddressType.Legacy Blockchain.CardanoShelley -> CardanoAddressType.Byron else -> null } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt index 273d52d384..f2d65a4fcf 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt @@ -12,6 +12,7 @@ import com.tangem.tap.common.extensions.toFormattedCurrencyString import com.tangem.tap.common.extensions.toFormattedFiatValue import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.model.WalletStoreModel +import com.tangem.tap.features.wallet.models.PendingTransactionType import com.tangem.tap.features.wallet.models.WalletWarning import com.tangem.tap.features.wallet.redux.WalletMainButton import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN @@ -19,9 +20,24 @@ import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import java.math.BigDecimal internal fun WalletDataModel.mainButton(blockchainAmount: BigDecimal): WalletMainButton = WalletMainButton.SendButton( - enabled = !isEmptyAmount && status.pendingTransactions.isEmpty() && !blockchainAmount.isZero(), + enabled = !isEmptyAmount && + hasPendingTransactions() && + !blockchainAmount.isZero(), ) +internal fun WalletDataModel.hasPendingTransactions(): Boolean { + // for now check pending ongoing only just for BTC, later test and add other utxo networks + val isBitcoinBlockchain = + currency.blockchain == Blockchain.Bitcoin || currency.blockchain == Blockchain.BitcoinTestnet + if (currency.isBlockchain() && isBitcoinBlockchain) { + val outgoingTransactions = status.pendingTransactions.filter { + it.type == PendingTransactionType.Outgoing + } + return outgoingTransactions.isEmpty() + } + return status.pendingTransactions.isEmpty() +} + internal fun WalletDataModel.getFormattedAmount(): String { return status.amount.toFormattedCurrencyString( decimals = currency.decimals, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt index afd1ba8c88..d89d245766 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt @@ -4,14 +4,7 @@ import android.content.Context import android.util.AttributeSet import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* import androidx.compose.material.Divider import androidx.compose.material.Surface import androidx.compose.material.Text @@ -27,11 +20,7 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.SelectorButton -import com.tangem.core.ui.components.SpacerH12 -import com.tangem.core.ui.components.SpacerH4 -import com.tangem.core.ui.components.SpacerW16 -import com.tangem.core.ui.components.SpacerW4 +import com.tangem.core.ui.components.* import com.tangem.core.ui.res.TangemTheme import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.domain.model.TotalFiatBalance @@ -42,7 +31,8 @@ import java.math.BigDecimal import java.math.RoundingMode import java.text.DecimalFormat import java.text.NumberFormat -import java.util.* +import java.util.Currency +import java.util.Locale internal class TotalBalanceCard @JvmOverloads constructor( context: Context, @@ -125,7 +115,7 @@ private fun TotalBalanceCardContent(state: TotalBalanceCardState, modifier: Modi -> LoadedAmount( amount = buildAmountString( amount = state.amount, - fiatCurrencySymbol = state.fiatCurrency.symbol, + fiatCurrency = state.fiatCurrency, ), ) } @@ -228,30 +218,45 @@ private fun LoadedAmount(amount: AnnotatedString, modifier: Modifier = Modifier) } @Composable -private fun buildAmountString(amount: BigDecimal?, fiatCurrencySymbol: String): AnnotatedString { +private fun buildAmountString(amount: BigDecimal?, fiatCurrency: FiatCurrency): AnnotatedString { if (amount == null) return AnnotatedString(text = UNKNOWN_AMOUNT_SIGN) - val formatter = NumberFormat.getInstance(Locale.getDefault()) as? DecimalFormat - ?: return AnnotatedString("${amount.toPlainString()} $fiatCurrencySymbol") - val decimalFormat = formatter.apply { - maximumFractionDigits = 2 - minimumFractionDigits = 2 - isGroupingUsed = true - this.roundingMode = RoundingMode.HALF_UP + val locale = Locale.getDefault() + val fractionDigits = 2 + val formatter = NumberFormat.getCurrencyInstance(locale) as? DecimalFormat + ?: return AnnotatedString("${amount.toPlainString()} ${fiatCurrency.symbol}") + + val currencyToShow = "${fiatCurrency.symbol} " + val scaledAmount = Currency.getInstance(fiatCurrency.code)?.let { currency -> + formatter.currency = currency + formatter.maximumFractionDigits = fractionDigits + formatter.minimumFractionDigits = fractionDigits + formatter.isGroupingUsed = true + formatter.roundingMode = RoundingMode.HALF_UP + formatter.format(amount).replace(currency.symbol, currencyToShow) + } ?: formatter.format(amount) + + val integer = scaledAmount.substringBefore(formatter.decimalFormatSymbols.decimalSeparator) + var reminder = scaledAmount.substringAfter(formatter.decimalFormatSymbols.decimalSeparator) + + // if locale formatted currency at the end, remember it and place out of AnnotatedString + val currency = if (reminder.endsWith(currencyToShow)) { + reminder = reminder.dropLast(currencyToShow.length) + currencyToShow + } else { + "" } - val scaledAmount = decimalFormat.format(amount) - val integer = scaledAmount.substringBefore(decimalFormat.decimalFormatSymbols.decimalSeparator) - val reminder = scaledAmount.substringAfter(decimalFormat.decimalFormatSymbols.decimalSeparator) return buildAnnotatedString { append(integer) - append(decimalFormat.decimalFormatSymbols.decimalSeparator) + append(formatter.decimalFormatSymbols.decimalSeparator) append( AnnotatedString( - text = "$reminder $fiatCurrencySymbol", + text = reminder, spanStyle = TangemTheme.typography.h3.toSpanStyle(), ), ) + append(currency) // it is not empty if was placed at the end after locale formatting } } diff --git a/app/src/main/res/drawable-hdpi/ic_eth.png b/app/src/main/res/drawable-hdpi/ic_eth.png deleted file mode 100644 index 9db27f814e..0000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_eth.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_eth.webp b/app/src/main/res/drawable-hdpi/ic_eth.webp new file mode 100644 index 0000000000..ffe1bc6185 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_eth.webp differ diff --git a/app/src/main/res/drawable-hdpi/meet_tangem.png b/app/src/main/res/drawable-hdpi/meet_tangem.png deleted file mode 100644 index 25f2d5a3ba..0000000000 Binary files a/app/src/main/res/drawable-hdpi/meet_tangem.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/meet_tangem.webp b/app/src/main/res/drawable-hdpi/meet_tangem.webp new file mode 100644 index 0000000000..e9b4ef6b19 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/meet_tangem.webp differ diff --git a/app/src/main/res/drawable-hdpi/revolutionary_wallet.png b/app/src/main/res/drawable-hdpi/revolutionary_wallet.png deleted file mode 100644 index f4f384a54d..0000000000 Binary files a/app/src/main/res/drawable-hdpi/revolutionary_wallet.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/revolutionary_wallet.webp b/app/src/main/res/drawable-hdpi/revolutionary_wallet.webp new file mode 100644 index 0000000000..7e0f631f9c Binary files /dev/null and b/app/src/main/res/drawable-hdpi/revolutionary_wallet.webp differ diff --git a/app/src/main/res/drawable-hdpi/wallet_for_everyone.png b/app/src/main/res/drawable-hdpi/wallet_for_everyone.png deleted file mode 100644 index 6eec2f53dd..0000000000 Binary files a/app/src/main/res/drawable-hdpi/wallet_for_everyone.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/wallet_for_everyone.webp b/app/src/main/res/drawable-hdpi/wallet_for_everyone.webp new file mode 100644 index 0000000000..e50874dfbf Binary files /dev/null and b/app/src/main/res/drawable-hdpi/wallet_for_everyone.webp differ diff --git a/app/src/main/res/drawable-ldpi/ic_eth.png b/app/src/main/res/drawable-ldpi/ic_eth.png deleted file mode 100644 index b8cd7b26ab..0000000000 Binary files a/app/src/main/res/drawable-ldpi/ic_eth.png and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/ic_eth.webp b/app/src/main/res/drawable-ldpi/ic_eth.webp new file mode 100644 index 0000000000..dc633be363 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_eth.webp differ diff --git a/app/src/main/res/drawable-ldpi/ic_qcx.png b/app/src/main/res/drawable-ldpi/ic_qcx.png deleted file mode 100644 index 68f2ded287..0000000000 Binary files a/app/src/main/res/drawable-ldpi/ic_qcx.png and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/ic_qcx.webp b/app/src/main/res/drawable-ldpi/ic_qcx.webp new file mode 100644 index 0000000000..9cea22a163 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_qcx.webp differ diff --git a/app/src/main/res/drawable-mdpi/ic_eth.png b/app/src/main/res/drawable-mdpi/ic_eth.png deleted file mode 100644 index e01e76371f..0000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_eth.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_eth.webp b/app/src/main/res/drawable-mdpi/ic_eth.webp new file mode 100644 index 0000000000..befd41d571 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_eth.webp differ diff --git a/app/src/main/res/drawable-mdpi/meet_tangem.png b/app/src/main/res/drawable-mdpi/meet_tangem.png deleted file mode 100644 index ef04e35830..0000000000 Binary files a/app/src/main/res/drawable-mdpi/meet_tangem.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/meet_tangem.webp b/app/src/main/res/drawable-mdpi/meet_tangem.webp new file mode 100644 index 0000000000..7bd5ec80c5 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/meet_tangem.webp differ diff --git a/app/src/main/res/drawable-mdpi/revolutionary_wallet.png b/app/src/main/res/drawable-mdpi/revolutionary_wallet.png deleted file mode 100644 index 4dba3bffa4..0000000000 Binary files a/app/src/main/res/drawable-mdpi/revolutionary_wallet.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/revolutionary_wallet.webp b/app/src/main/res/drawable-mdpi/revolutionary_wallet.webp new file mode 100644 index 0000000000..dfc7b106a9 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/revolutionary_wallet.webp differ diff --git a/app/src/main/res/drawable-mdpi/wallet_for_everyone.png b/app/src/main/res/drawable-mdpi/wallet_for_everyone.png deleted file mode 100644 index 9ec8c92210..0000000000 Binary files a/app/src/main/res/drawable-mdpi/wallet_for_everyone.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/wallet_for_everyone.webp b/app/src/main/res/drawable-mdpi/wallet_for_everyone.webp new file mode 100644 index 0000000000..bf0ea6cf64 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/wallet_for_everyone.webp differ diff --git a/app/src/main/res/drawable-xhdpi/ic_eth.png b/app/src/main/res/drawable-xhdpi/ic_eth.png deleted file mode 100644 index 5e5807bf8b..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_eth.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_eth.webp b/app/src/main/res/drawable-xhdpi/ic_eth.webp new file mode 100644 index 0000000000..ba04cee99d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_eth.webp differ diff --git a/app/src/main/res/drawable-xhdpi/meet_tangem.png b/app/src/main/res/drawable-xhdpi/meet_tangem.png deleted file mode 100644 index 958f83e0a6..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/meet_tangem.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/meet_tangem.webp b/app/src/main/res/drawable-xhdpi/meet_tangem.webp new file mode 100644 index 0000000000..9bc7cba662 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/meet_tangem.webp differ diff --git a/app/src/main/res/drawable-xhdpi/revolutionary_wallet.png b/app/src/main/res/drawable-xhdpi/revolutionary_wallet.png deleted file mode 100644 index e72d2ff281..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/revolutionary_wallet.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/revolutionary_wallet.webp b/app/src/main/res/drawable-xhdpi/revolutionary_wallet.webp new file mode 100644 index 0000000000..ee0435caf1 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/revolutionary_wallet.webp differ diff --git a/app/src/main/res/drawable-xhdpi/wallet_for_everyone.png b/app/src/main/res/drawable-xhdpi/wallet_for_everyone.png deleted file mode 100644 index 773a3bb463..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/wallet_for_everyone.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/wallet_for_everyone.webp b/app/src/main/res/drawable-xhdpi/wallet_for_everyone.webp new file mode 100644 index 0000000000..2eec0c7074 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/wallet_for_everyone.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_eth.png b/app/src/main/res/drawable-xxhdpi/ic_eth.png deleted file mode 100644 index 1e7a0e7952..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_eth.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_eth.webp b/app/src/main/res/drawable-xxhdpi/ic_eth.webp new file mode 100644 index 0000000000..440f1fea5c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_eth.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/meet_tangem.png b/app/src/main/res/drawable-xxhdpi/meet_tangem.png deleted file mode 100644 index 4d5144346d..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/meet_tangem.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/meet_tangem.webp b/app/src/main/res/drawable-xxhdpi/meet_tangem.webp new file mode 100644 index 0000000000..941a671afb Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/meet_tangem.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/revolutionary_wallet.png b/app/src/main/res/drawable-xxhdpi/revolutionary_wallet.png deleted file mode 100644 index 83a8bc7963..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/revolutionary_wallet.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/revolutionary_wallet.webp b/app/src/main/res/drawable-xxhdpi/revolutionary_wallet.webp new file mode 100644 index 0000000000..360f004149 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/revolutionary_wallet.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/wallet_for_everyone.png b/app/src/main/res/drawable-xxhdpi/wallet_for_everyone.png deleted file mode 100644 index 18b099256b..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/wallet_for_everyone.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/wallet_for_everyone.webp b/app/src/main/res/drawable-xxhdpi/wallet_for_everyone.webp new file mode 100644 index 0000000000..408f94af76 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/wallet_for_everyone.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_eth.png b/app/src/main/res/drawable-xxxhdpi/ic_eth.png deleted file mode 100644 index 74ead90795..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_eth.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_eth.webp b/app/src/main/res/drawable-xxxhdpi/ic_eth.webp new file mode 100644 index 0000000000..8a3417ce7b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_eth.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/meet_tangem.png b/app/src/main/res/drawable-xxxhdpi/meet_tangem.png deleted file mode 100644 index a84bb7b804..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/meet_tangem.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/meet_tangem.webp b/app/src/main/res/drawable-xxxhdpi/meet_tangem.webp new file mode 100644 index 0000000000..8ca89394bc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/meet_tangem.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/revolutionary_wallet.png b/app/src/main/res/drawable-xxxhdpi/revolutionary_wallet.png deleted file mode 100644 index bc0d94be7f..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/revolutionary_wallet.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/revolutionary_wallet.webp b/app/src/main/res/drawable-xxxhdpi/revolutionary_wallet.webp new file mode 100644 index 0000000000..9568833439 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/revolutionary_wallet.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/wallet_for_everyone.png b/app/src/main/res/drawable-xxxhdpi/wallet_for_everyone.png deleted file mode 100644 index 1d99d407ab..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/wallet_for_everyone.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/wallet_for_everyone.webp b/app/src/main/res/drawable-xxxhdpi/wallet_for_everyone.webp new file mode 100644 index 0000000000..5dc7baec82 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/wallet_for_everyone.webp differ diff --git a/app/src/main/res/drawable/currency0.png b/app/src/main/res/drawable/currency0.png deleted file mode 100644 index 0d293ec8bb..0000000000 Binary files a/app/src/main/res/drawable/currency0.png and /dev/null differ diff --git a/app/src/main/res/drawable/currency0.webp b/app/src/main/res/drawable/currency0.webp new file mode 100644 index 0000000000..a55afc36fe Binary files /dev/null and b/app/src/main/res/drawable/currency0.webp differ diff --git a/app/src/main/res/drawable/currency1.png b/app/src/main/res/drawable/currency1.png deleted file mode 100644 index 37144efffe..0000000000 Binary files a/app/src/main/res/drawable/currency1.png and /dev/null differ diff --git a/app/src/main/res/drawable/currency1.webp b/app/src/main/res/drawable/currency1.webp new file mode 100644 index 0000000000..b24e1009ff Binary files /dev/null and b/app/src/main/res/drawable/currency1.webp differ diff --git a/app/src/main/res/drawable/currency2.png b/app/src/main/res/drawable/currency2.png deleted file mode 100644 index bd75790221..0000000000 Binary files a/app/src/main/res/drawable/currency2.png and /dev/null differ diff --git a/app/src/main/res/drawable/currency2.webp b/app/src/main/res/drawable/currency2.webp new file mode 100644 index 0000000000..86dfb0f2d4 Binary files /dev/null and b/app/src/main/res/drawable/currency2.webp differ diff --git a/app/src/main/res/drawable/currency3.png b/app/src/main/res/drawable/currency3.png deleted file mode 100644 index c517578807..0000000000 Binary files a/app/src/main/res/drawable/currency3.png and /dev/null differ diff --git a/app/src/main/res/drawable/currency3.webp b/app/src/main/res/drawable/currency3.webp new file mode 100644 index 0000000000..a6a5da8499 Binary files /dev/null and b/app/src/main/res/drawable/currency3.webp differ diff --git a/app/src/main/res/drawable/currency4.png b/app/src/main/res/drawable/currency4.png deleted file mode 100644 index 9a39d5651d..0000000000 Binary files a/app/src/main/res/drawable/currency4.png and /dev/null differ diff --git a/app/src/main/res/drawable/currency4.webp b/app/src/main/res/drawable/currency4.webp new file mode 100644 index 0000000000..517c55c0f7 Binary files /dev/null and b/app/src/main/res/drawable/currency4.webp differ diff --git a/app/src/main/res/drawable/dapps0.png b/app/src/main/res/drawable/dapps0.png deleted file mode 100644 index 6152b1215b..0000000000 Binary files a/app/src/main/res/drawable/dapps0.png and /dev/null differ diff --git a/app/src/main/res/drawable/dapps0.webp b/app/src/main/res/drawable/dapps0.webp new file mode 100644 index 0000000000..65809f5fe4 Binary files /dev/null and b/app/src/main/res/drawable/dapps0.webp differ diff --git a/app/src/main/res/drawable/dapps1.png b/app/src/main/res/drawable/dapps1.png deleted file mode 100644 index 2d4effaf8b..0000000000 Binary files a/app/src/main/res/drawable/dapps1.png and /dev/null differ diff --git a/app/src/main/res/drawable/dapps1.webp b/app/src/main/res/drawable/dapps1.webp new file mode 100644 index 0000000000..833c952010 Binary files /dev/null and b/app/src/main/res/drawable/dapps1.webp differ diff --git a/app/src/main/res/drawable/dapps2.png b/app/src/main/res/drawable/dapps2.png deleted file mode 100644 index f7643c4ec7..0000000000 Binary files a/app/src/main/res/drawable/dapps2.png and /dev/null differ diff --git a/app/src/main/res/drawable/dapps2.webp b/app/src/main/res/drawable/dapps2.webp new file mode 100644 index 0000000000..a13cb38ba8 Binary files /dev/null and b/app/src/main/res/drawable/dapps2.webp differ diff --git a/app/src/main/res/drawable/dapps3.png b/app/src/main/res/drawable/dapps3.png deleted file mode 100644 index 9e0110179b..0000000000 Binary files a/app/src/main/res/drawable/dapps3.png and /dev/null differ diff --git a/app/src/main/res/drawable/dapps3.webp b/app/src/main/res/drawable/dapps3.webp new file mode 100644 index 0000000000..99f594fd3d Binary files /dev/null and b/app/src/main/res/drawable/dapps3.webp differ diff --git a/app/src/main/res/drawable/dapps4.png b/app/src/main/res/drawable/dapps4.png deleted file mode 100644 index 4eae3c0985..0000000000 Binary files a/app/src/main/res/drawable/dapps4.png and /dev/null differ diff --git a/app/src/main/res/drawable/dapps4.webp b/app/src/main/res/drawable/dapps4.webp new file mode 100644 index 0000000000..15704d6a6e Binary files /dev/null and b/app/src/main/res/drawable/dapps4.webp differ diff --git a/app/src/main/res/drawable/dapps5.png b/app/src/main/res/drawable/dapps5.png deleted file mode 100644 index 0c581624c7..0000000000 Binary files a/app/src/main/res/drawable/dapps5.png and /dev/null differ diff --git a/app/src/main/res/drawable/dapps5.webp b/app/src/main/res/drawable/dapps5.webp new file mode 100644 index 0000000000..5d6370f05a Binary files /dev/null and b/app/src/main/res/drawable/dapps5.webp differ diff --git a/app/src/main/res/drawable/ic_voyr.png b/app/src/main/res/drawable/ic_voyr.png deleted file mode 100644 index 545990d041..0000000000 Binary files a/app/src/main/res/drawable/ic_voyr.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_voyr.webp b/app/src/main/res/drawable/ic_voyr.webp new file mode 100644 index 0000000000..2343599d4b Binary files /dev/null and b/app/src/main/res/drawable/ic_voyr.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100755 index e307de6027..0000000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000..553cf934c1 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100755 index cfab76de31..0000000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000..166f2867c7 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100755 index bddcb01270..0000000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000..e143972134 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100755 index ac5cb152bb..0000000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000..8c9245318b Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100755 index 533177434c..0000000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000000..3b746049eb Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png deleted file mode 100644 index d32c73f660..0000000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp new file mode 100644 index 0000000000..541a4842d7 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 9b72259299..0000000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000..6b7e439344 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 4d71fe324b..6b0879d8e1 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -46,6 +46,8 @@ Чат Принять Добавить + Применить + Одобрение Внимание Баланс: %s биометрическую аутентификацию @@ -73,7 +75,9 @@ Начать Отправить Успешно + Обмен условия участия + Перевод Я понял Да Адрес контракта скопирован! @@ -255,6 +259,9 @@ Восстановление кода доступа Идентичные карты Код доступа + Группировка + По балансу + Сортировка токенов Участвовать Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже. Не удалось загрузить информацию по реферальной программе. Код ошибки: %s. Пожалуйста, попробуйте позже. @@ -382,7 +389,6 @@ Безлимитно Открыть в обозревателе В процессе - Обмен Обменять Обмен %s на Котировки включают дополнительную комиссию Tangem в размере %s. Это помогает нам предоставлять первоклассный продукт. diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index 461c90bc77..ab1801d759 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -69,6 +69,7 @@ 開始 提交 成功 + 交換 條款和條件 我了解 @@ -367,7 +368,6 @@ 要繼續,您需要允許 1inch 智能合約使用您的 %s 在瀏覽器中查看 進行中 - 交換 交易 交易 %s 至 此外,報價包括%s的 Tangem 費用。這有助於我們提供一流的產品 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 695c74084b..405c31d86a 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -44,6 +44,8 @@ Support Accept Add + Apply + Approval Attention Balance: %s biometric authentication @@ -71,7 +73,9 @@ Start Submit Success + Swap terms and conditions + Transfer I understand Yes Contract address copied! @@ -253,6 +257,9 @@ Access code restore Identical cards Access code + Group + By balance + Organize tokens Participate Failed to load the information about the referral program. Please try again later. Failed to load the information about the referral program. Error code: %s. Please try again later. @@ -378,7 +385,6 @@ Unlimited View in Explorer In progress - Swap Swap Swap of %s to Quotes include an additional Tangem commission of %s. This helps us deliver a top-of-the-line product. diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 1dc31c8335..94bb016bdc 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -9,8 +9,10 @@ dependencies { implementation(deps.androidx.fragment.ktx) /** Compose */ + implementation(deps.compose.constraintLayout) implementation(deps.compose.foundation) implementation(deps.compose.material) + implementation(deps.compose.material3) implementation(deps.compose.ui.tooling) /** Other libraries */ diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt index 00a6b2ac03..5ca9a16de8 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt @@ -312,14 +312,8 @@ private fun TangemButton( textStyle: TextStyle = TangemTheme.typography.button, ) { Button( - modifier = modifier - .width(IntrinsicSize.Min) - .heightIn(min = size.toHeightDp()), - onClick = { - if (!showProgress) { - onClick() - } - }, + modifier = modifier.heightIn(min = size.toHeightDp()), + onClick = { if (!showProgress) onClick() }, enabled = enabled, elevation = elevation, shape = size.toShape(), @@ -588,59 +582,44 @@ private open class TangemButtonColors( // region Preview @Composable -private fun PrimaryButtonSample(modifier: Modifier = Modifier) { +private fun PrimaryButtonSample() { Column( - modifier = modifier - .background(TangemTheme.colors.background.primary), + modifier = Modifier.background(TangemTheme.colors.background.primary), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { - PrimaryButton( - modifier = Modifier.fillMaxWidth(), - text = "Manage tokens", - onClick = { /* no-op */ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - PrimaryButton( - modifier = Modifier.fillMaxWidth(), - showProgress = true, - text = "Manage tokens", - onClick = { /* no-op */ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) + PrimaryButton(modifier = Modifier.fillMaxWidth(), text = "Manage tokens", onClick = { }) + PrimaryButton(modifier = Modifier.fillMaxWidth(), showProgress = true, text = "Manage tokens", onClick = { }) PrimaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", iconResId = R.drawable.ic_tangem_24, - onClick = { /* no-op */ }, + onClick = { }, ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) PrimaryButtonIconStart( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", iconResId = R.drawable.ic_tangem_24, - onClick = { /* no-op */ }, + onClick = { }, ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) PrimaryButton( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", enabled = false, - onClick = { /* no-op */ }, + onClick = { }, ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) PrimaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", iconResId = R.drawable.ic_tangem_24, enabled = false, - onClick = { /* no-op */ }, + onClick = { }, ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) PrimaryButtonIconStart( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", iconResId = R.drawable.ic_tangem_24, enabled = false, - onClick = { /* no-op */ }, + onClick = { }, ) } } @@ -662,59 +641,44 @@ private fun PrimaryButtonPreview_Dark() { } @Composable -private fun SecondaryButtonSample(modifier: Modifier = Modifier) { +private fun SecondaryButtonSample() { Column( - modifier = modifier - .background(TangemTheme.colors.background.primary), + modifier = Modifier.background(TangemTheme.colors.background.primary), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { - SecondaryButton( - modifier = Modifier.fillMaxWidth(), - text = "Manage tokens", - onClick = { /* no-op */ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - SecondaryButton( - modifier = Modifier.fillMaxWidth(), - showProgress = true, - text = "Manage tokens", - onClick = { /* no-op */ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) + SecondaryButton(modifier = Modifier.fillMaxWidth(), text = "Manage tokens", onClick = { }) + SecondaryButton(modifier = Modifier.fillMaxWidth(), showProgress = true, text = "Manage tokens", onClick = { }) SecondaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", iconResId = R.drawable.ic_tangem_24, - onClick = { /* no-op */ }, + onClick = { }, ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) SecondaryButtonIconStart( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", iconResId = R.drawable.ic_tangem_24, - onClick = { /* no-op */ }, + onClick = { }, ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) SecondaryButton( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", enabled = false, - onClick = { /* no-op */ }, + onClick = { }, ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) SecondaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", iconResId = R.drawable.ic_tangem_24, enabled = false, - onClick = { /* no-op */ }, + onClick = { }, ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) SecondaryButtonIconStart( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", iconResId = R.drawable.ic_tangem_24, enabled = false, - onClick = { /* no-op */ }, + onClick = { }, ) } } @@ -736,50 +700,23 @@ private fun SecondaryButtonPreview_Dark() { } @Composable -private fun TextButtonSample(modifier: Modifier = Modifier) { +private fun TextButtonSample() { Column( - modifier = modifier - .background(TangemTheme.colors.background.primary), + modifier = Modifier.background(TangemTheme.colors.background.primary), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { - TextButton( - text = "Enabled", - onClick = { /* no-op */ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - TextButtonIconStart( - text = "Enabled", - iconResId = R.drawable.ic_plus_24, - onClick = { /* no-op */ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - TextButton( - text = "Enabled", - enabled = false, - onClick = { /* no-op */ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - TextButtonIconStart( - text = "Enabled", - iconResId = R.drawable.ic_plus_24, - enabled = false, - onClick = { /* no-op */ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - WarningTextButton( - text = "Delete", - onClick = { /* no-op */ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - SelectorButton( - text = "USD", - onClick = { /* no-op */ }, - ) + TextButton(text = "Enabled", onClick = { }) + TextButtonIconStart(text = "Enabled", iconResId = R.drawable.ic_plus_24, onClick = { }) + TextButton(text = "Enabled", enabled = false, onClick = { }) + TextButtonIconStart(text = "Enabled", iconResId = R.drawable.ic_plus_24, enabled = false, onClick = { }) + WarningTextButton(text = "Delete", onClick = { }) + SelectorButton(text = "USD", onClick = { }) } } @Preview(showBackground = true, widthDp = 360) @Composable -private fun TextButtonPreview_Light() { +private fun TextButtonPreview_LightTheme() { TangemTheme { TextButtonSample() } @@ -787,62 +724,30 @@ private fun TextButtonPreview_Light() { @Preview(showBackground = true, widthDp = 360) @Composable -private fun TextButtonPreview_Dark() { +private fun TextButtonPreview_DarkTheme() { TangemTheme(isDark = true) { TextButtonSample() } } @Composable -private fun ActionButtonSample(modifier: Modifier = Modifier) { +private fun ActionButtonSample() { Column( - modifier = modifier - .background(TangemTheme.colors.background.primary), + modifier = Modifier.background(TangemTheme.colors.background.primary), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { - RoundedActionButton( - text = "Send", - iconResId = R.drawable.ic_arrow_up_24, - onClick = { /* [REDACTED_TODO_COMMENT]*/ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - ActionButton( - text = "Send", - iconResId = R.drawable.ic_arrow_up_24, - onClick = { /* [REDACTED_TODO_COMMENT]*/ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - BackgroundActionButton( - text = "Send", - iconResId = R.drawable.ic_arrow_up_24, - onClick = { /* [REDACTED_TODO_COMMENT]*/ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - RoundedActionButton( - text = "Send", - iconResId = R.drawable.ic_arrow_up_24, - enabled = false, - onClick = { /* [REDACTED_TODO_COMMENT]*/ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - ActionButton( - text = "Send", - iconResId = R.drawable.ic_arrow_up_24, - enabled = false, - onClick = { /* [REDACTED_TODO_COMMENT]*/ }, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - BackgroundActionButton( - text = "Send", - iconResId = R.drawable.ic_arrow_up_24, - enabled = false, - onClick = { /* [REDACTED_TODO_COMMENT]*/ }, - ) + RoundedActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, onClick = { }) + ActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, onClick = { }) + BackgroundActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, onClick = { }) + RoundedActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { }) + ActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { }) + BackgroundActionButton(text = "Send", iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { }) } } @Preview(showBackground = true, widthDp = 360) @Composable -private fun ActionButtonPreview_Light() { +private fun ActionButtonPreview_LightTheme() { TangemTheme { ActionButtonSample() } @@ -850,7 +755,7 @@ private fun ActionButtonPreview_Light() { @Preview(showBackground = true, widthDp = 360) @Composable -private fun ActionButtonPreview_Dark() { +private fun ActionButtonPreview_DarkTheme() { TangemTheme(isDark = true) { ActionButtonSample() } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt new file mode 100644 index 0000000000..0f265ecce9 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt @@ -0,0 +1,342 @@ +package com.tangem.core.ui.components.transactions + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Dimension +import com.tangem.core.ui.R +import com.tangem.core.ui.components.CircleShimmer +import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.res.TangemTheme + +/** + * Transaction component + * + * @param state state + * + * @see Figma Component + * +[REDACTED_AUTHOR] + */ +@Composable +fun Transaction(state: TransactionState) { + Surface( + modifier = Modifier + .background(TangemTheme.colors.background.primary) + .defaultMinSize(minHeight = TangemTheme.dimens.size56) + .padding(horizontal = TangemTheme.dimens.spacing12, vertical = TangemTheme.dimens.spacing10), + color = TangemTheme.colors.background.primary, + ) { + @Suppress("DestructuringDeclarationWithTooManyEntries") + ConstraintLayout(modifier = Modifier.fillMaxWidth()) { + val (iconItem, titleItem, subtitleItem, amountItem, timestampItem) = createRefs() + + Icon( + state = state, + modifier = Modifier + .size(TangemTheme.dimens.size40) + .constrainAs(iconItem) { + start.linkTo(parent.start) + centerVerticallyTo(parent) + }, + ) + + Title( + state = state, + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing12) + .constrainAs(titleItem) { + start.linkTo(iconItem.end) + top.linkTo(iconItem.top) + }, + ) + + Subtitle( + state = state, + modifier = Modifier + .padding(top = TangemTheme.dimens.spacing6) + .padding(horizontal = TangemTheme.dimens.spacing12) + .constrainAs(subtitleItem) { + start.linkTo(iconItem.end) + top.linkTo(amountItem.bottom) + end.linkTo(timestampItem.start) + width = Dimension.fillToConstraints + }, + ) + + Amount( + state = state, + modifier = Modifier.constrainAs(amountItem) { + start.linkTo(titleItem.end) + top.linkTo(titleItem.top) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + }, + ) + + Timestamp( + state = state, + modifier = Modifier + .padding(top = TangemTheme.dimens.spacing6) + .constrainAs(timestampItem) { + top.linkTo(amountItem.bottom) + end.linkTo(parent.end) + }, + ) + } + } +} + +@Composable +private fun Icon(state: TransactionState, modifier: Modifier = Modifier) { + when (state) { + is TransactionState.Content -> { + Box( + modifier = modifier + .size(TangemTheme.dimens.size40) + .background( + color = when (state) { + is TransactionState.ProcessedTransactionContent -> { + TangemTheme.colors.icon.attention.copy(alpha = 0.1f) + } + is TransactionState.CompletedTransactionContent -> { + TangemTheme.colors.background.secondary + } + }, + shape = CircleShape, + ), + ) { + Icon( + painter = painterResource( + id = when (state) { + is TransactionState.Sending, + is TransactionState.Send, + -> R.drawable.ic_arrow_up_24 + + is TransactionState.Receiving, + is TransactionState.Receive, + -> R.drawable.ic_arrow_down_24 + + is TransactionState.Approving, + is TransactionState.Approved, + -> R.drawable.ic_doc_24 + + is TransactionState.Swapping, + is TransactionState.Swapped, + -> R.drawable.ic_exchange_vertical_24 + }, + ), + contentDescription = null, + modifier = Modifier + .size(TangemTheme.dimens.size20) + .align(Alignment.Center), + tint = when (state) { + is TransactionState.ProcessedTransactionContent -> TangemTheme.colors.icon.attention + is TransactionState.CompletedTransactionContent -> TangemTheme.colors.icon.informative + }, + ) + } + } + is TransactionState.Loading -> { + CircleShimmer(modifier = modifier.size(TangemTheme.dimens.size40)) + } + } +} + +@Composable +private fun Title(state: TransactionState, modifier: Modifier = Modifier) { + when (state) { + is TransactionState.ProcessedTransactionContent -> { + Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6)) { + Text( + text = stringResource( + id = when (state) { + is TransactionState.Sending -> R.string.common_transfer + is TransactionState.Receiving -> R.string.common_transfer + is TransactionState.Approving -> R.string.common_approval + is TransactionState.Swapping -> R.string.common_swap + }, + ), + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.subtitle2, + ) + + Image( + modifier = Modifier.align(Alignment.CenterVertically), + painter = painterResource(id = R.drawable.img_loader_15), + contentDescription = null, + ) + } + } + is TransactionState.CompletedTransactionContent -> { + Text( + text = stringResource( + id = when (state) { + is TransactionState.Send -> R.string.common_transfer + is TransactionState.Receive -> R.string.common_transfer + is TransactionState.Approved -> R.string.common_approval + is TransactionState.Swapped -> R.string.common_swap + }, + ), + modifier = modifier, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.subtitle2, + ) + } + is TransactionState.Loading -> { + RectangleShimmer( + modifier = modifier.size(width = TangemTheme.dimens.size70, height = TangemTheme.dimens.size12), + ) + } + } +} + +@Composable +private fun Subtitle(state: TransactionState, modifier: Modifier = Modifier) { + when (state) { + is TransactionState.Content -> { + Text( + text = when (state) { + is TransactionState.Sending -> "to: ${state.address}" + is TransactionState.Send -> "to: ${state.address}" + is TransactionState.Receiving -> "from: ${state.address}" + is TransactionState.Receive -> "from: ${state.address}" + is TransactionState.Approving -> "from: ${state.address}" + is TransactionState.Approved -> "from: ${state.address}" + is TransactionState.Swapping -> "contract: ${state.address}" + is TransactionState.Swapped -> "contract ${state.address}" + }, + modifier = modifier, + textAlign = TextAlign.Start, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.caption, + ) + } + is TransactionState.Loading -> { + RectangleShimmer( + modifier = modifier.size(width = TangemTheme.dimens.size52, height = TangemTheme.dimens.size12), + ) + } + } +} + +@Composable +private fun Amount(state: TransactionState, modifier: Modifier = Modifier) { + when (state) { + is TransactionState.Content -> { + Text( + text = state.amount, + modifier = modifier, + textAlign = TextAlign.End, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.body2, + ) + } + is TransactionState.Loading -> { + RectangleShimmer( + modifier = modifier.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12), + ) + } + } +} + +@Composable +private fun Timestamp(state: TransactionState, modifier: Modifier = Modifier) { + when (state) { + is TransactionState.Content -> { + Text( + text = state.timestamp, + modifier = modifier, + textAlign = TextAlign.End, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.caption, + ) + } + is TransactionState.Loading -> { + RectangleShimmer( + modifier = modifier.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12), + ) + } + } +} + +@Preview +@Composable +private fun Preview_TransactionItem_LightTheme( + @PreviewParameter(TransactionItemStateProvider::class) state: TransactionState, +) { + TangemTheme(isDark = false) { + Transaction(state) + } +} + +@Preview +@Composable +private fun Preview_TransactionItem_DarkTheme( + @PreviewParameter(TransactionItemStateProvider::class) state: TransactionState, +) { + TangemTheme(isDark = true) { + Transaction(state) + } +} + +private class TransactionItemStateProvider : CollectionPreviewParameterProvider( + collection = listOf( + TransactionState.Sending( + address = "33BddS...ga2B", + amount = "-0.500913 BTC", + timestamp = "8:41", + ), + TransactionState.Receiving( + address = "33BddS...ga2B", + amount = "+0.500913 BTC", + timestamp = "8:41", + ), + TransactionState.Approving( + address = "33BddS...ga2B", + amount = "+0.500913 BTC", + timestamp = "8:41", + ), + TransactionState.Swapping( + address = "33BddS...ga2B", + amount = "+0.500913 BTC", + timestamp = "8:41", + ), + TransactionState.Send( + address = "33BddS...ga2B", + amount = "-0.500913 BTC", + timestamp = "8:41", + ), + TransactionState.Receive( + address = "33BddS...ga2B", + amount = "+0.500913 BTC", + timestamp = "8:41", + ), + TransactionState.Approved( + address = "33BddS...ga2B", + amount = "+0.500913 BTC", + timestamp = "8:41", + ), + TransactionState.Swapped( + address = "33BddS...ga2B", + amount = "+0.500913 BTC", + timestamp = "8:41", + ), + TransactionState.Loading, + ), +) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionState.kt new file mode 100644 index 0000000000..f0502a4a21 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionState.kt @@ -0,0 +1,155 @@ +package com.tangem.core.ui.components.transactions + +/** + * Transaction component state + * +[REDACTED_AUTHOR] + */ +sealed interface TransactionState { + + /** + * Content state + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + sealed class Content( + open val address: String, + open val amount: String, + open val timestamp: String, + ) : TransactionState + + /** + * Content state for processed transaction + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + sealed class ProcessedTransactionContent( + override val address: String, + override val amount: String, + override val timestamp: String, + ) : Content(address, amount, timestamp) + + /** + * Content state for completed transaction + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + sealed class CompletedTransactionContent( + override val address: String, + override val amount: String, + override val timestamp: String, + ) : Content(address, amount, timestamp) + + /** + * Processed sending transaction state + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + data class Sending( + override val address: String, + override val amount: String, + override val timestamp: String, + ) : ProcessedTransactionContent(address, amount, timestamp) + + /** + * Processed receiving transaction state + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + data class Receiving( + override val address: String, + override val amount: String, + override val timestamp: String, + ) : ProcessedTransactionContent(address, amount, timestamp) + + /** + * Processed approving transaction state + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + data class Approving( + override val address: String, + override val amount: String, + override val timestamp: String, + ) : ProcessedTransactionContent(address, amount, timestamp) + + /** + * Processed swapping transaction state + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + data class Swapping( + override val address: String, + override val amount: String, + override val timestamp: String, + ) : ProcessedTransactionContent(address, amount, timestamp) + + /** + * Completed sending transaction state + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + data class Send( + override val address: String, + override val amount: String, + override val timestamp: String, + ) : CompletedTransactionContent(address, amount, timestamp) + + /** + * Completed receiving transaction state + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + data class Receive( + override val address: String, + override val amount: String, + override val timestamp: String, + ) : CompletedTransactionContent(address, amount, timestamp) + + /** + * Completed approving transaction state + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + data class Approved( + override val address: String, + override val amount: String, + override val timestamp: String, + ) : CompletedTransactionContent(address, amount, timestamp) + + /** + * Completed swapping transaction state + * + * @property address address + * @property amount amount + * @property timestamp timestamp + */ + data class Swapped( + override val address: String, + override val amount: String, + override val timestamp: String, + ) : CompletedTransactionContent(address, amount, timestamp) + + /** Loading state */ + object Loading : TransactionState +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt index e70b0d7015..a27dcb58c4 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt @@ -2,22 +2,42 @@ package com.tangem.core.ui.extensions import androidx.annotation.StringRes import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.res.stringResource +/** + * Utility class for creating text as [String] or [StringRes]. + * It necessary to use [Immutable] annotation because all sealed interface has runtime stability. + * All subclasses are stable. + */ +@Immutable sealed interface TextReference { - class Res(@StringRes val id: Int, val formatArgs: List = emptyList()) : TextReference { - constructor(@StringRes id: Int, vararg formatArgs: Any) : this(id, formatArgs.toList()) - } - class Str(val value: String) : TextReference + /** + * Text resource id + * + * @property id resource id + * @property formatArgs arguments + * + * Impossible to use [kotlinx.collections.immutable.ImmutableList] because [Any] is unstable. + */ + data class Res(@StringRes val id: Int, val formatArgs: WrappedList = WrappedList(emptyList())) : TextReference + + /** + * Text string + * + * @property value value + */ + data class Str(val value: String) : TextReference } +/** Get text */ @Composable @ReadOnlyComposable fun TextReference.resolveReference(): String { return when (this) { - is TextReference.Res -> stringResource(this.id, *this.formatArgs.toTypedArray()) - is TextReference.Str -> this.value + is TextReference.Res -> stringResource(id, *formatArgs.toTypedArray()) + is TextReference.Str -> value } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/WrappedList.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/WrappedList.kt new file mode 100644 index 0000000000..6f529c9574 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/WrappedList.kt @@ -0,0 +1,10 @@ +package com.tangem.core.ui.extensions + +import androidx.compose.runtime.Immutable + +/** +[REDACTED_AUTHOR] + */ +@JvmInline +@Immutable +value class WrappedList(val data: List) : List by data \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index c2fbac9891..421c82c9e1 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -56,9 +56,11 @@ data class TangemDimens internal constructor( val size46: Dp = 46.dp, val size48: Dp = 48.dp, val size50: Dp = 50.dp, + val size52: Dp = 52.dp, val size56: Dp = 56.dp, val size62: Dp = 62.dp, val size68: Dp = 68.dp, + val size70: Dp = 70.dp, val size72: Dp = 72.dp, val size80: Dp = 80.dp, val size84: Dp = 84.dp, diff --git a/core/ui/src/main/res/drawable/ic_doc_24.xml b/core/ui/src/main/res/drawable/ic_doc_24.xml new file mode 100644 index 0000000000..8855153e59 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_doc_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_filter_24.xml b/core/ui/src/main/res/drawable/ic_filter_24.xml new file mode 100644 index 0000000000..b88ca7dcef --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_filter_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_group_24.xml b/core/ui/src/main/res/drawable/ic_group_24.xml new file mode 100644 index 0000000000..0f5f5f4487 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_group_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_sort_24.xml b/core/ui/src/main/res/drawable/ic_sort_24.xml new file mode 100644 index 0000000000..538a24d8e0 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_sort_24.xml @@ -0,0 +1,9 @@ + + + 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 f5a39df29e..acad315b28 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 @@ -54,7 +54,7 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick: Column { AppBarWithBackButton( - text = stringResource(R.string.swapping_swap), + text = stringResource(R.string.common_swap), onBackClick = state.onBackClicked, iconRes = R.drawable.ic_close_24, ) @@ -348,7 +348,7 @@ private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> U else -> { PrimaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), - text = stringResource(id = R.string.swapping_swap), + text = stringResource(id = R.string.common_swap), iconResId = R.drawable.ic_tangem_24, enabled = state.swapButton.enabled, showProgress = state.swapButton.loading, @@ -384,7 +384,7 @@ private val receiveCard = SwapCardData( coinId = "", ) -val stateSelectable = SelectableItemsState( +val stateSelectable = SelectableItemsState( selectedItem = Item( 0, TextReference.Str("Balance"), diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt index d8ad3e646a..2d6f3e52b4 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt @@ -39,7 +39,7 @@ fun SwapSuccessScreen(state: SwapSuccessStateHolder, onBack: () -> Unit) { }, topBar = { AppBarWithBackButton( - text = stringResource(R.string.swapping_swap), + text = stringResource(R.string.common_swap), onBackClick = onBack, iconRes = R.drawable.ic_close_24, ) diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 183e43c174..c639f23ac8 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -22,6 +22,8 @@ dependencies { implementation(deps.compose.ui.tooling) implementation(deps.compose.shimmer) implementation(deps.compose.accompanist.systemUiController) + + /** Other libraries */ implementation(deps.kotlin.immutable.collections) /** DI */ diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt index 04e93bf88f..954467d0fe 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt @@ -7,6 +7,8 @@ import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment +import com.tangem.core.ui.components.SystemBarsEffect +import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.features.wallet.navigation.WalletRouter @@ -38,8 +40,15 @@ internal class WalletFragment : Fragment() { return ComposeView(inflater.context).apply { setContent { - isTransitionGroup = true - _walletRouter.Initialize() + TangemTheme { + val systemBarsColor = TangemTheme.colors.background.secondary + SystemBarsEffect { + setSystemBarsColor(systemBarsColor) + } + + isTransitionGroup = true + _walletRouter.Initialize() + } } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 40817e750e..92c8d8f644 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -1,19 +1,23 @@ package com.tangem.feature.wallet.presentation.common import com.tangem.core.ui.R -import com.tangem.feature.wallet.presentation.common.state.NetworkGroupState import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState -import com.tangem.feature.wallet.presentation.common.state.TokenListState +import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.WalletContentItemState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList import java.util.UUID internal object WalletPreviewData { - val walletCardContent = WalletCardState.Content( + val walletTopBarConfig = WalletStateHolder.TopBarConfig(onScanCardClick = {}, onMoreClick = {}) + + val walletCardContentState = WalletCardState.Content( id = UUID.randomUUID().toString(), title = "Wallet 1", balance = "8923,05 $", @@ -22,7 +26,7 @@ internal object WalletPreviewData { onClick = {}, ) - val walletCardLoading = WalletCardState.Loading( + val walletCardLoadingState = WalletCardState.Loading( id = UUID.randomUUID().toString(), title = "Wallet 1", additionalInfo = "3 cards • Seed enabled", @@ -30,7 +34,7 @@ internal object WalletPreviewData { onClick = {}, ) - val walletCardHiddenContent = WalletCardState.HiddenContent( + val walletCardHiddenContentState = WalletCardState.HiddenContent( id = UUID.randomUUID().toString(), title = "Wallet 1", additionalInfo = "3 cards • Seed enabled", @@ -38,7 +42,7 @@ internal object WalletPreviewData { onClick = {}, ) - val walletCardError = WalletCardState.Error( + val walletCardErrorState = WalletCardState.Error( id = UUID.randomUUID().toString(), title = "Wallet 1", additionalInfo = "3 cards • Seed enabled", @@ -46,15 +50,6 @@ internal object WalletPreviewData { onClick = {}, ) - val walletScreenState = WalletStateHolder( - onBackClick = {}, - headerConfig = WalletStateHolder.HeaderConfig( - wallets = persistentListOf(walletCardContent, walletCardLoading, walletCardHiddenContent, walletCardError), - onScanCardClick = {}, - onMoreClick = {}, - ), - ) - val tokenItemVisibleState = TokenItemState.Content( id = UUID.randomUUID().toString(), tokenIconUrl = null, @@ -107,9 +102,46 @@ internal object WalletPreviewData { val loadingTokenItemState = TokenItemState.Loading(id = UUID.randomUUID().toString()) - val organizeTokensState = OrganizeTokensStateHolder( - tokens = TokenListState.GroupedByNetwork( - groups = persistentListOf(), + private const val networksSize = 10 + private const val tokensSize = 3 + val draggableItems = List(networksSize) { it } + .flatMap { index -> + val n = index + 1 + + val group = DraggableItem.GroupHeader( + id = "group_$n", + networkName = "$n", + ) + + val tokens: MutableList = mutableListOf() + repeat(times = tokensSize) { i -> + val nt = i + 1 + tokens.add( + DraggableItem.Token( + tokenItemState = tokenItemDragState.copy( + id = "${group.id}_token_$nt", + name = "Token $nt", + networkIconResId = R.drawable.img_eth_22.takeIf { i != 0 }, + ), + groupId = group.id, + ), + ) + } + + buildList { + add(group) + addAll(tokens) + } + } + .toPersistentList() + + val draggableTokens = draggableItems + .filterIsInstance() + .toPersistentList() + + val groupedOrganizeTokensState = OrganizeTokensStateHolder( + itemsState = OrganizeTokensListState.GroupedByNetwork( + items = draggableItems, ), header = OrganizeTokensStateHolder.HeaderConfig( onSortByBalanceClick = {}, @@ -121,57 +153,71 @@ internal object WalletPreviewData { ), ) - private val draggableTokenList = persistentListOf( - tokenItemDragState.copy( - id = "token_1", - name = "Ethereum", - tokenIconResId = R.drawable.img_eth_22, - networkIconResId = null, - fiatAmount = "3 172,14 $", - ), - tokenItemDragState.copy( - id = "token_2", - networkIconResId = R.drawable.img_eth_22, - fiatAmount = "803,65 $", - ), - tokenItemDragState.copy( - id = "token_3", - name = "USDT", - tokenIconResId = R.drawable.img_arbitrum_22, - networkIconResId = R.drawable.img_eth_22, - fiatAmount = "88,01 $", + val organizeTokensState = groupedOrganizeTokensState.copy( + itemsState = OrganizeTokensListState.Ungrouped( + items = draggableTokens, ), ) - val networkGroup = NetworkGroupState.Content( - id = UUID.randomUUID().toString(), - networkName = "Ethereum", - tokens = persistentListOf( - tokenItemVisibleState.copy( - id = "token_1", - name = "Ethereum", - tokenIconResId = R.drawable.img_eth_22, - networkIconResId = null, - amount = "1,89340821 ETH", + val groupedWalletScreenState = WalletStateHolder( + onBackClick = {}, + topBarConfig = walletTopBarConfig, + selectedWallet = walletCardContentState, + wallets = persistentListOf( + walletCardContentState, + walletCardLoadingState, + walletCardHiddenContentState, + walletCardErrorState, + ), + contentItems = persistentListOf( + WalletContentItemState.NetworkGroupTitle("Bitcoin"), + WalletContentItemState.Token( + tokenItemVisibleState.copy( + id = "token_1", + name = "Ethereum", + tokenIconResId = R.drawable.img_eth_22, + networkIconResId = null, + amount = "1,89340821 ETH", + ), ), - tokenItemVisibleState.copy( - id = "token_2", - networkIconResId = R.drawable.img_eth_22, - amount = "733,71097 MATIC", + WalletContentItemState.Token( + tokenItemVisibleState.copy( + id = "token_2", + name = "Ethereum", + tokenIconResId = R.drawable.img_eth_22, + networkIconResId = null, + amount = "1,89340821 ETH", + ), ), - tokenItemVisibleState.copy( - id = "token_3", - name = "USDT", - tokenIconResId = R.drawable.img_arbitrum_22, - networkIconResId = R.drawable.img_eth_22, - amount = "0,25404523 ARB", + WalletContentItemState.Token( + tokenItemVisibleState.copy( + id = "token_3", + name = "Ethereum", + tokenIconResId = R.drawable.img_eth_22, + networkIconResId = null, + amount = "1,89340821 ETH", + ), + ), + WalletContentItemState.Token( + tokenItemVisibleState.copy( + id = "token_4", + name = "Ethereum", + tokenIconResId = R.drawable.img_eth_22, + networkIconResId = null, + amount = "1,89340821 ETH", + ), + ), + WalletContentItemState.NetworkGroupTitle("Ethereum"), + WalletContentItemState.Token( + tokenItemVisibleState.copy( + id = "token_5", + name = "Ethereum", + tokenIconResId = R.drawable.img_eth_22, + networkIconResId = null, + amount = "1,89340821 ETH", + ), ), ), - ) - - val draggableNetworkGroup = NetworkGroupState.Draggable( - id = UUID.randomUUID().toString(), - networkName = "Ethereum", - tokens = draggableTokenList, + onOrganizeTokensClick = {}, ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt index ee2d924e85..82e40737c5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt @@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.* import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.key import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -15,65 +14,83 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.common.state.NetworkGroupState @Composable -internal fun NetworkGroupItem(state: NetworkGroupState, modifier: Modifier = Modifier) { +internal fun NetworkGroupItem(networkName: String, modifier: Modifier = Modifier) { + InternalNetworkGroupItem( + modifier = modifier, + networkName = networkName, + ) +} + +@Composable +internal fun DraggableNetworkGroupItem(networkName: String, modifier: Modifier = Modifier) { + InternalNetworkGroupItem( + modifier = modifier, + networkName = networkName, + endIcon = { + Icon( + painter = painterResource(id = R.drawable.ic_group_drop_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, + ) + }, + ) +} + +@Composable +private fun InternalNetworkGroupItem( + networkName: String, + modifier: Modifier = Modifier, + endIcon: @Composable RowScope.() -> Unit = {}, +) { Column(modifier = modifier) { Row( modifier = Modifier .background(TangemTheme.colors.background.primary) - .padding(horizontal = TangemTheme.dimens.spacing12) + .padding(horizontal = TangemTheme.dimens.spacing14) .fillMaxWidth() .heightIn(min = TangemTheme.dimens.size48), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = stringResource(id = R.string.wallet_network_group_title, state.networkName), + text = stringResource(id = R.string.wallet_network_group_title, networkName), style = TangemTheme.typography.subtitle2, color = TangemTheme.colors.text.tertiary, ) - if (state is NetworkGroupState.Draggable) { - Icon( - painter = painterResource(id = R.drawable.ic_group_drop_24), - tint = TangemTheme.colors.icon.informative, - contentDescription = null, - ) - } - } - Column { - state.tokens.forEach { token -> - key(token.id) { - TokenItem(state = token) - } - } + endIcon() } } } // region Preview +@Composable +private fun NetworkGroupItemSample(isDraggable: Boolean) { + if (isDraggable) { + DraggableNetworkGroupItem(networkName = "Ethereum") + } else { + NetworkGroupItem(networkName = "Ethereum") + } +} + @Preview(showBackground = true, widthDp = 360) @Composable -private fun NetworkGroupItemPreview_Light(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) { +private fun NetworkGroupItemPreview_Light(@PreviewParameter(NetworkGroupProvider::class) isDraggable: Boolean) { TangemTheme { - NetworkGroupItem(group) + NetworkGroupItemSample(isDraggable) } } @Preview(showBackground = true, widthDp = 360) @Composable -private fun NetworkGroupItemPreview_Dark(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) { +private fun NetworkGroupItemPreview_Dark(@PreviewParameter(NetworkGroupProvider::class) isDraggable: Boolean) { TangemTheme(isDark = true) { - NetworkGroupItem(group) + NetworkGroupItemSample(isDraggable) } } -private class NetworkGroupProvider : CollectionPreviewParameterProvider( - collection = listOf( - WalletPreviewData.networkGroup, - WalletPreviewData.draggableNetworkGroup, - ), +private class NetworkGroupProvider : CollectionPreviewParameterProvider( + collection = listOf(true, false), ) // endregion Preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt index 78c5b5f322..de8daa9cb8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt @@ -9,6 +9,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.Stable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -18,6 +19,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstrainedLayoutReference import androidx.constraintlayout.compose.ConstraintLayout @@ -34,6 +36,10 @@ import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState private const val DOTS = "•••" +val TOKEN_ITEM_HEIGHT: Dp + @Composable + @ReadOnlyComposable + get() = TangemTheme.dimens.size68 @Composable internal fun TokenItem(state: TokenItemState, modifier: Modifier = Modifier) { @@ -76,7 +82,8 @@ internal fun DraggableTokenItem(state: TokenItemState.Draggable, modifier: Modif hasPending = false, options = { ref -> Icon( - modifier = Modifier.constrainAsOptionsItem(scope = this, ref), + modifier = Modifier + .constrainAsOptionsItem(scope = this, ref), painter = painterResource(id = R.drawable.ic_drag_24), tint = TangemTheme.colors.icon.informative, contentDescription = null, @@ -199,7 +206,7 @@ private fun InternalTokenItem( modifier = Modifier .fillMaxWidth() .padding( - horizontal = TangemTheme.dimens.spacing12, + horizontal = TangemTheme.dimens.spacing14, vertical = TangemTheme.dimens.spacing4, ), ) { @@ -251,7 +258,7 @@ private fun BaseSurface( content: @Composable () -> Unit, ) { Surface( - modifier = modifier.defaultMinSize(minHeight = TangemTheme.dimens.size68), + modifier = modifier.defaultMinSize(minHeight = TOKEN_ITEM_HEIGHT), color = TangemTheme.colors.background.primary, onClick = onClick ?: {}, enabled = onClick != null, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/NetworkGroupState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/NetworkGroupState.kt deleted file mode 100644 index c34fdd126d..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/NetworkGroupState.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.tangem.feature.wallet.presentation.common.state - -import androidx.compose.runtime.Immutable -import kotlinx.collections.immutable.ImmutableList - -@Immutable -internal sealed interface NetworkGroupState { - val id: String - val networkName: String - val tokens: ImmutableList - - data class Draggable( - override val id: String, - override val networkName: String, - override val tokens: ImmutableList, - ) : NetworkGroupState - - data class Content( - override val id: String, - override val networkName: String, - override val tokens: ImmutableList, - ) : NetworkGroupState -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt deleted file mode 100644 index fe3b7fba46..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.feature.wallet.presentation.common.state - -import androidx.compose.runtime.Immutable -import kotlinx.collections.immutable.ImmutableList - -@Immutable -internal sealed interface TokenListState { - - data class GroupedByNetwork( - val groups: ImmutableList, - ) : TokenListState - - data class Ungrouped( - val tokens: ImmutableList, - ) : TokenListState -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt index f4b5bb65d0..fb6be5831b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt @@ -1,89 +1,256 @@ -// TODO: Remove after components implementation -@file:Suppress("UNUSED_PARAMETER") - package com.tangem.feature.wallet.presentation.organizetokens +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.material3.* +import androidx.compose.foundation.lazy.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.AppBarDefaults +import androidx.compose.material3.FabPosition +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import com.tangem.core.ui.components.BackgroundActionButton +import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.SecondaryButton import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.common.state.TokenListState +import com.tangem.feature.wallet.presentation.common.component.DraggableNetworkGroupItem +import com.tangem.feature.wallet.presentation.common.component.DraggableTokenItem @Composable internal fun OrganizeTokensScreen(state: OrganizeTokensStateHolder, modifier: Modifier = Modifier) { + val tokensListState = rememberLazyListState() + Scaffold( modifier = modifier, topBar = { - TopBar(state.header) + TopBar(state.header, tokensListState) }, content = { paddingValues -> TokenList( - state = state.tokens, modifier = Modifier.padding(paddingValues), + listState = tokensListState, + state = state.itemsState, ) }, floatingActionButtonPosition = FabPosition.Center, floatingActionButton = { Actions(state.actions) }, - contentColor = TangemTheme.colors.background.primary, + containerColor = TangemTheme.colors.background.secondary, ) } @Composable -private fun TokenList(state: TokenListState, modifier: Modifier = Modifier) { - when (state) { - is TokenListState.GroupedByNetwork -> { - /* [REDACTED_TODO_COMMENT] */ - } - is TokenListState.Ungrouped -> { - /* [REDACTED_TODO_COMMENT] */ +private fun TokenList(listState: LazyListState, state: OrganizeTokensListState, modifier: Modifier = Modifier) { + Box(modifier = modifier) { + LazyColumn( + modifier = Modifier + .align(Alignment.TopCenter) + .padding(horizontal = TangemTheme.dimens.spacing16), + state = listState, + contentPadding = PaddingValues( + top = TangemTheme.dimens.spacing12, + bottom = TangemTheme.dimens.spacing92, + ), + ) { + itemsIndexed( + items = state.items, + key = { _, item -> item.id }, + ) { index, item -> + DraggableItem( + item = item, + index = index, + lastItemIndex = state.items.lastIndex, + ) + } } + + BottomGradient(modifier = Modifier.align(Alignment.BottomCenter)) } } -@OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar(config: OrganizeTokensStateHolder.HeaderConfig, modifier: Modifier = Modifier) { - TopAppBar( - title = { +private fun DraggableItem(index: Int, item: DraggableItem, lastItemIndex: Int) { + val itemModifier = Modifier + .clipFirstAndLastItems(index, lastItemIndex) + + when (item) { + is DraggableItem.GroupHeader -> DraggableNetworkGroupItem( + modifier = itemModifier, + networkName = item.networkName, + ) + is DraggableItem.Token -> DraggableTokenItem( + modifier = itemModifier, + state = item.tokenItemState, + ) + } +} + +@Composable +private fun BottomGradient(modifier: Modifier = Modifier) { + Box( + modifier = modifier + .fillMaxWidth() + .height(TangemTheme.dimens.size116) + .background( + brush = Brush.verticalGradient( + colors = listOf( + Color.Transparent, + TangemTheme.colors.background.secondary, + ), + ), + ), + ) +} + +@Composable +private fun TopBar( + config: OrganizeTokensStateHolder.HeaderConfig, + tokensListState: LazyListState, + modifier: Modifier = Modifier, +) { + val isElevationEnabled by remember { + derivedStateOf { + tokensListState.firstVisibleItemScrollOffset > 0 + } + } + val elevation by animateDpAsState( + targetValue = if (isElevationEnabled) AppBarDefaults.TopAppBarElevation else TangemTheme.dimens.elevation0, + label = "top_bar_shadow_elevation", + ) + + Column( + modifier = modifier + .shadow(elevation) + .background(TangemTheme.colors.background.secondary) + .padding(horizontal = TangemTheme.dimens.spacing16) + .fillMaxWidth(), + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size56), + contentAlignment = Alignment.CenterStart, + ) { Text( - text = "Organize tokens", // TODO: Move to resources + text = stringResource(id = R.string.organize_tokens_title), style = TangemTheme.typography.subtitle1, color = TangemTheme.colors.text.primary1, maxLines = 1, ) - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = TangemTheme.colors.background.secondary, - titleContentColor = TangemTheme.colors.text.primary1, - ), - ) + } + Row( + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size56), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), + ) { + BackgroundActionButton( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.organize_tokens_sort_by_balance), + iconResId = R.drawable.ic_sort_24, + onClick = config.onSortByBalanceClick, + ) + BackgroundActionButton( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.organize_tokens_group), + iconResId = R.drawable.ic_group_24, + onClick = config.onGroupByNetworkClick, + ) + } + } } @Composable private fun Actions(config: OrganizeTokensStateHolder.ActionsConfig, modifier: Modifier = Modifier) { - // TODO: Implement list apply and cancel actions + Row( + modifier = modifier + .padding(horizontal = TangemTheme.dimens.spacing16) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), + ) { + SecondaryButton( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.common_cancel), + onClick = config.onCancelClick, + ) + PrimaryButton( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.common_apply), + onClick = config.onApplyClick, + ) + } +} + +private fun Modifier.clipFirstAndLastItems(index: Int, lastItemIndex: Int): Modifier = composed { + when (index) { + 0 -> { + this + .clip( + RoundedCornerShape( + topStart = TangemTheme.dimens.radius16, + topEnd = TangemTheme.dimens.radius16, + ), + ) + } + lastItemIndex -> { + this + .clip( + RoundedCornerShape( + bottomStart = TangemTheme.dimens.radius16, + bottomEnd = TangemTheme.dimens.radius16, + ), + ) + } + else -> this + } } // region Preview @Preview(showBackground = true, widthDp = 360) @Composable -private fun OrganizeTokensScreenPreview_Light() { +private fun OrganizeTokensScreenPreview_Light( + @PreviewParameter(OrganizeTokensStateProvider::class) state: OrganizeTokensStateHolder, +) { TangemTheme { - OrganizeTokensScreen(state = WalletPreviewData.organizeTokensState) + OrganizeTokensScreen(state) } } @Preview(showBackground = true, widthDp = 360) @Composable -private fun OrganizeTokensScreenPreview_Dark() { +private fun OrganizeTokensScreenPreview_Dark( + @PreviewParameter(OrganizeTokensStateProvider::class) state: OrganizeTokensStateHolder, +) { TangemTheme(isDark = true) { - OrganizeTokensScreen(state = WalletPreviewData.organizeTokensState) + OrganizeTokensScreen(state) } } + +private class OrganizeTokensStateProvider : CollectionPreviewParameterProvider( + collection = listOf( + WalletPreviewData.organizeTokensState, + WalletPreviewData.groupedOrganizeTokensState, + ), +) // endregion Preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt index b9a8e797cb..0e0a07d2e6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt @@ -1,10 +1,13 @@ package com.tangem.feature.wallet.presentation.organizetokens -import com.tangem.feature.wallet.presentation.common.state.TokenListState +import androidx.compose.runtime.Immutable +import com.tangem.feature.wallet.presentation.common.state.TokenItemState +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toPersistentList internal data class OrganizeTokensStateHolder( - val tokens: TokenListState, val header: HeaderConfig, + val itemsState: OrganizeTokensListState, val actions: ActionsConfig, ) { @@ -17,4 +20,44 @@ internal data class OrganizeTokensStateHolder( val onApplyClick: () -> Unit, val onCancelClick: () -> Unit, ) +} + +@Immutable +internal sealed class OrganizeTokensListState { + abstract val items: ImmutableList + + data class GroupedByNetwork( + override val items: ImmutableList, + ) : OrganizeTokensListState() + + data class Ungrouped( + override val items: ImmutableList, + ) : OrganizeTokensListState() + + @Suppress("UNCHECKED_CAST") + inline fun updateItems(update: (List) -> List): OrganizeTokensListState { + val updatedItems = update(this.items).toPersistentList() + + return when (this) { + is GroupedByNetwork -> this.copy(items = updatedItems) + is Ungrouped -> this.copy(items = updatedItems as ImmutableList) + } + } +} + +@Immutable +internal sealed interface DraggableItem { + val id: String + + data class GroupHeader( + override val id: String, + val networkName: String, + ) : DraggableItem + + data class Token( + val tokenItemState: TokenItemState.Draggable, + val groupId: String, + ) : DraggableItem { + override val id: String = tokenItemState.id + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt index 16f726eab1..cbdd25e47e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt @@ -5,11 +5,14 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import com.tangem.feature.wallet.presentation.common.WalletPreviewData +import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder.HeaderConfig import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.toPersistentList import javax.inject.Inject import kotlin.properties.Delegates +// FIXME: Implemented with preview data @HiltViewModel internal class OrganizeTokensViewModel @Inject constructor() : ViewModel() { @@ -18,5 +21,26 @@ internal class OrganizeTokensViewModel @Inject constructor() : ViewModel() { var uiState: OrganizeTokensStateHolder by mutableStateOf(getInitialState()) private set - private fun getInitialState(): OrganizeTokensStateHolder = WalletPreviewData.organizeTokensState + private fun getInitialState(): OrganizeTokensStateHolder = WalletPreviewData.organizeTokensState.copy( + itemsState = OrganizeTokensListState.Ungrouped( + items = WalletPreviewData.draggableTokens, + ), + header = HeaderConfig( + onSortByBalanceClick = { /* no-op */ }, + onGroupByNetworkClick = this::toggleTokensByNetworkGrouping, + ), + ) + + private fun toggleTokensByNetworkGrouping() { + val newListState = when (val itemsState = uiState.itemsState) { + is OrganizeTokensListState.GroupedByNetwork -> OrganizeTokensListState.Ungrouped( + items = itemsState.items.filterIsInstance().toPersistentList(), + ) + is OrganizeTokensListState.Ungrouped -> OrganizeTokensListState.GroupedByNetwork( + items = WalletPreviewData.draggableItems, + ) + } + + uiState = uiState.copy(itemsState = newListState) + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index b963f8f7b7..2976a7b8db 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -10,7 +10,6 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.WalletFragment import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt index 7685a267da..2f000581e2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt @@ -1,9 +1,12 @@ package com.tangem.feature.wallet.presentation.wallet.state import androidx.annotation.DrawableRes +import androidx.compose.runtime.Immutable /** Wallet card state */ +@Immutable internal sealed interface WalletCardState { + /** Id */ val id: String diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletContentItemState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletContentItemState.kt new file mode 100644 index 0000000000..66a426f04a --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletContentItemState.kt @@ -0,0 +1,13 @@ +package com.tangem.feature.wallet.presentation.wallet.state + +import com.tangem.feature.wallet.presentation.common.state.TokenItemState + +/** +[REDACTED_AUTHOR] + */ +internal sealed interface WalletContentItemState { + + data class NetworkGroupTitle(val networkName: String) : WalletContentItemState + + data class Token(val tokenItemState: TokenItemState) : WalletContentItemState +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt index 8142667ad3..061647e21b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt @@ -5,26 +5,28 @@ import kotlinx.collections.immutable.ImmutableList /** * Wallet state holder * - * @property onBackClick lambda be invoked when back button is clicked - * @property headerConfig header config + * @property onBackClick lambda be invoked when back button is clicked + * @property topBarConfig top bar config + * @property selectedWallet selected wallet + * @property wallets list of wallets states + * @property contentItems content items * [REDACTED_AUTHOR] */ internal data class WalletStateHolder( val onBackClick: () -> Unit, - val headerConfig: HeaderConfig, + val topBarConfig: TopBarConfig, + val selectedWallet: WalletCardState, + val wallets: ImmutableList, + val contentItems: ImmutableList, + val onOrganizeTokensClick: () -> Unit, ) { /** - * Header config + * Top bar config * - * @property wallets list of wallets states * @property onScanCardClick lambda be invoked when scan card button is clicked * @property onMoreClick lambda be invoked when more button is clicked */ - data class HeaderConfig( - val wallets: ImmutableList, - val onScanCardClick: () -> Unit, - val onMoreClick: () -> Unit, - ) + data class TopBarConfig(val onScanCardClick: () -> Unit, val onMoreClick: () -> Unit) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 098d1e1e45..963c6b4a1a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -1,11 +1,32 @@ package com.tangem.feature.wallet.presentation.wallet.ui import androidx.activity.compose.BackHandler -import androidx.compose.material.Scaffold +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import com.tangem.core.ui.components.RoundedActionButton +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.common.WalletPreviewData +import com.tangem.feature.wallet.presentation.common.component.NetworkGroupItem +import com.tangem.feature.wallet.presentation.common.component.TokenItem +import com.tangem.feature.wallet.presentation.wallet.state.WalletContentItemState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletHeader +import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletCardsList +import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletTopBar /** * Wallet screen @@ -19,9 +40,109 @@ internal fun WalletScreen(state: WalletStateHolder, modifier: Modifier = Modifie BackHandler(onBack = state.onBackClick) Scaffold( - modifier = modifier, - topBar = { WalletHeader(config = state.headerConfig) }, - ) { - // TODO: [REDACTED_TASK_KEY] Design a body with tokens and transactions + topBar = { WalletTopBar(config = state.topBarConfig) }, + containerColor = TangemTheme.colors.background.secondary, + ) { scaffoldPaddings -> + val lastContentItemIndex = remember(state.contentItems) { + state.contentItems.lastIndex + } + + LazyColumn( + modifier = Modifier + .padding(scaffoldPaddings) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + item { WalletCardsList(wallets = state.wallets) } + + itemsIndexed( + items = state.contentItems, + key = { _, item -> + when (item) { + is WalletContentItemState.NetworkGroupTitle -> item.networkName + is WalletContentItemState.Token -> item.tokenItemState.id + } + }, + ) { index, item -> + val itemModifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing16) + .clipFirstAndLastItems(index, lastContentItemIndex) + + when (item) { + is WalletContentItemState.NetworkGroupTitle -> { + NetworkGroupItem( + networkName = item.networkName, + modifier = itemModifier, + ) + } + is WalletContentItemState.Token -> { + TokenItem( + state = item.tokenItemState, + modifier = itemModifier, + ) + } + } + } + + item { + RoundedActionButton( + text = stringResource(id = R.string.organize_tokens_title), + iconResId = R.drawable.ic_filter_24, + onClick = state.onOrganizeTokensClick, + modifier = Modifier.padding(top = TangemTheme.dimens.spacing14), + ) + } + } } -} \ No newline at end of file +} + +private fun Modifier.clipFirstAndLastItems(index: Int, lastItemIndex: Int): Modifier = composed { + when (index) { + 0 -> { + this + .padding(top = TangemTheme.dimens.spacing14) + .clip( + RoundedCornerShape( + topStart = TangemTheme.dimens.radius16, + topEnd = TangemTheme.dimens.radius16, + ), + ) + } + lastItemIndex -> { + this + .clip( + RoundedCornerShape( + bottomStart = TangemTheme.dimens.radius16, + bottomEnd = TangemTheme.dimens.radius16, + ), + ) + } + else -> this + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun WalletScreenPreview_Light( + @PreviewParameter(WalletScreenParameterProvider::class) state: WalletStateHolder, +) { + TangemTheme { + WalletScreen(state) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun WalletScreenPreview_Dark( + @PreviewParameter(WalletScreenParameterProvider::class) state: WalletStateHolder, +) { + TangemTheme(isDark = true) { + WalletScreen(state) + } +} + +private class WalletScreenParameterProvider : CollectionPreviewParameterProvider( + collection = listOf(WalletPreviewData.groupedWalletScreenState), +) +// endregion Preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt index 562c954023..d92651ac6f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt @@ -44,7 +44,7 @@ internal fun WalletCard(state: WalletCardState, modifier: Modifier = Modifier) { ConstraintLayout( modifier = Modifier .fillMaxWidth() - .padding(horizontal = TangemTheme.dimens.spacing12), + .padding(horizontal = TangemTheme.dimens.spacing14), ) { val (balanceBlock, imageItem) = createRefs() Column( @@ -182,10 +182,10 @@ private fun Preview_WalletCard_DarkTheme(@PreviewParameter(WalletCardStateProvid private class WalletCardStateProvider : CollectionPreviewParameterProvider( collection = listOf( - WalletPreviewData.walletCardContent, - WalletPreviewData.walletCardLoading, - WalletPreviewData.walletCardHiddenContent, - WalletPreviewData.walletCardError, + WalletPreviewData.walletCardContentState, + WalletPreviewData.walletCardLoadingState, + WalletPreviewData.walletCardHiddenContentState, + WalletPreviewData.walletCardErrorState, ), ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCardsList.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCardsList.kt new file mode 100644 index 0000000000..d37d359252 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCardsList.kt @@ -0,0 +1,78 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components + +import androidx.compose.animation.core.* +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.wallet.presentation.common.WalletPreviewData +import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +/** + * Wallets list + * + * @param wallets list of wallet state + * +[REDACTED_AUTHOR] + */ +@OptIn(ExperimentalFoundationApi::class) +@Composable +internal fun WalletCardsList(wallets: ImmutableList) { + val horizontalCardPadding = TangemTheme.dimens.spacing16 + val itemWidth = LocalConfiguration.current.screenWidthDp.dp - horizontalCardPadding * 2 + + val lazyListState = rememberLazyListState() + LazyRow( + modifier = Modifier.background(color = TangemTheme.colors.background.secondary), + state = lazyListState, + contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), + flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState), + ) { + items(items = wallets, key = WalletCardState::id) { state -> + WalletCard(state = state, modifier = Modifier.width(itemWidth)) + } + } +} + +@Preview +@Composable +private fun Preview_WalletHeader_LightTheme() { + TangemTheme(isDark = false) { + WalletCardsList( + wallets = persistentListOf( + WalletPreviewData.walletCardContentState, + WalletPreviewData.walletCardLoadingState, + WalletPreviewData.walletCardHiddenContentState, + WalletPreviewData.walletCardErrorState, + ), + ) + } +} + +@Preview +@Composable +private fun Preview_WalletHeader_DarkTheme() { + TangemTheme(isDark = true) { + WalletCardsList( + wallets = persistentListOf( + WalletPreviewData.walletCardContentState, + WalletPreviewData.walletCardLoadingState, + WalletPreviewData.walletCardHiddenContentState, + WalletPreviewData.walletCardErrorState, + ), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletHeader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletHeader.kt deleted file mode 100644 index cb01b48377..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletHeader.kt +++ /dev/null @@ -1,123 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.components - -import androidx.compose.animation.core.* -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.LocalOverscrollConfiguration -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider -import androidx.compose.ui.unit.dp -import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import kotlinx.collections.immutable.persistentListOf - -/** - * Wallet screen header - * - * @param config config - * -[REDACTED_AUTHOR] - */ -@OptIn(ExperimentalFoundationApi::class) -@Composable -internal fun WalletHeader(config: WalletStateHolder.HeaderConfig) { - Column( - modifier = Modifier - .background(color = TangemTheme.colors.background.secondary) - .padding(bottom = TangemTheme.dimens.spacing14), - ) { - TopBar(onScanCardClick = config.onScanCardClick, onMoreClick = config.onMoreClick) - - CompositionLocalProvider(LocalOverscrollConfiguration provides null) { - val lazyListState = rememberLazyListState() - LazyRow( - state = lazyListState, - contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), - flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState), - ) { - items(items = config.wallets, key = WalletCardState::id) { state -> - WalletCard( - state = state, - modifier = Modifier.width( - LocalConfiguration.current.screenWidthDp.dp - TangemTheme.dimens.size32, - ), - ) - } - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar(onScanCardClick: () -> Unit, onMoreClick: () -> Unit) { - TopAppBar( - title = { - Icon(painter = painterResource(id = R.drawable.img_tangem_logo_90_24), contentDescription = null) - }, - actions = { - IconButton(onClick = onScanCardClick) { - Icon(painter = painterResource(id = R.drawable.ic_tap_card_24), contentDescription = "Scan card") - } - IconButton(onClick = onMoreClick) { - Icon(painter = painterResource(id = R.drawable.ic_more_vertical_24), contentDescription = "More") - } - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = TangemTheme.colors.background.secondary, - titleContentColor = TangemTheme.colors.icon.primary1, - actionIconContentColor = TangemTheme.colors.icon.primary1, - ), - ) -} - -@Preview -@Composable -private fun Preview_WalletHeader_LightTheme( - @PreviewParameter(WalletHeaderProvider::class) state: WalletStateHolder.HeaderConfig, -) { - TangemTheme(isDark = false) { - WalletHeader(state) - } -} - -@Preview -@Composable -private fun Preview_WalletHeader_DarkTheme( - @PreviewParameter(WalletHeaderProvider::class) state: WalletStateHolder.HeaderConfig, -) { - TangemTheme(isDark = true) { - WalletHeader(state) - } -} - -private class WalletHeaderProvider : CollectionPreviewParameterProvider( - collection = listOf( - WalletStateHolder.HeaderConfig( - wallets = persistentListOf( - WalletPreviewData.walletCardContent, - WalletPreviewData.walletCardLoading, - WalletPreviewData.walletCardHiddenContent, - WalletPreviewData.walletCardError, - ), - onScanCardClick = {}, - onMoreClick = {}, - ), - ), -) \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletTopBar.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletTopBar.kt new file mode 100644 index 0000000000..29b91f97bd --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletTopBar.kt @@ -0,0 +1,55 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components + +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.common.WalletPreviewData +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder + +/** + * Wallet screen top bar + * + * @param config top bar config + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun WalletTopBar(config: WalletStateHolder.TopBarConfig) { + TopAppBar( + title = { + Icon(painter = painterResource(id = R.drawable.img_tangem_logo_90_24), contentDescription = null) + }, + actions = { + IconButton(onClick = config.onScanCardClick) { + Icon(painter = painterResource(id = R.drawable.ic_tap_card_24), contentDescription = "Scan card") + } + IconButton(onClick = config.onMoreClick) { + Icon(painter = painterResource(id = R.drawable.ic_more_vertical_24), contentDescription = "More") + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = TangemTheme.colors.background.secondary, + titleContentColor = TangemTheme.colors.icon.primary1, + actionIconContentColor = TangemTheme.colors.icon.primary1, + ), + scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(), + ) +} + +@Preview +@Composable +private fun Preview_WalletTopBar_LightTheme() { + TangemTheme(isDark = false) { + WalletTopBar(config = WalletPreviewData.walletTopBarConfig) + } +} + +@Preview +@Composable +private fun Preview_WalletTopBar_DarkTheme() { + TangemTheme(isDark = true) { + WalletTopBar(config = WalletPreviewData.walletTopBarConfig) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index a565bb8c91..9cb70bd530 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -27,9 +27,9 @@ internal class WalletViewModel @Inject constructor() : ViewModel() { private set // TODO: [REDACTED_TASK_KEY] Use production data instead of WalletPreviewData - private fun getInitialState(): WalletStateHolder = WalletPreviewData.walletScreenState.copy( + private fun getInitialState(): WalletStateHolder = WalletPreviewData.groupedWalletScreenState.copy( onBackClick = { router.popBackStack() }, - headerConfig = WalletPreviewData.walletScreenState.headerConfig.copy( + topBarConfig = WalletPreviewData.groupedWalletScreenState.topBarConfig.copy( onScanCardClick = { router.openOrganizeTokensScreen() }, ), ) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 8d89456b8c..269eb8f262 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -77,9 +77,9 @@ walletConnectWeb3 = "1.8.0" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_4.7-245" +tangemBlockchainSdk = "develop-247" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "release-app_4.7-268" +tangemCardSdk = "develop-266" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds # endregion Tangem