From 99b4c180cc9dec1ddb579d5b736e1ab707c2af66 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Jun 2023 14:26:31 +0300 Subject: [PATCH 1/4] Updated on 2026-08-14 --- build.gradle.kts | 25 +++++++++++++++++++ gradle/dependencies.toml | 12 ++++----- gradle/wrapper/gradle-wrapper.properties | 2 +- .../configuration/ConfigurationPlugin.kt | 8 +++--- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 91904f4d34..8ceb8869c1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -73,4 +73,29 @@ val assembleQA by tasks.registering { dependsOn(assembleInternalQA) dependsOn(assembleExternalQA) +} + +val generateComposeMetrics by tasks.registering { + group = "other" + description = "Build external APK and generates compose metrics to 'build/compose-metrics' directory" + + subprojects { + tasks.withType { + compilerOptions { + val outputDirectory = "${project.buildDir.absolutePath}/compose_metrics" + // Metrics + freeCompilerArgs.addAll( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=$outputDirectory", + ) + // Reports + freeCompilerArgs.addAll( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=$outputDirectory", + ) + } + } + } + + finalizedBy(assembleExternalQA) } \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index f7f6cb31df..5ad6f0a810 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -6,7 +6,7 @@ androidGradlePlugin = "7.4.2" firebaseCrashlytics = "2.9.4" googleServices = "4.3.10" -kotlin = "1.8.10" +kotlin = "1.8.21" # endregion Classpath # region AndroidX @@ -21,14 +21,14 @@ androidx-paging = "3.1.1" # endregion AndroidX # region Compose -compose-compiler = "1.4.3" -compose-runtime = "1.3.3" -compose-foundation = "1.3.1" -compose-material = "1.4.2" +compose-compiler = "1.4.7" +compose-runtime = "1.4.3" +compose-foundation = "1.4.3" +compose-material = "1.4.3" compose-material3 = "1.1.0" compose-constraint = "1.0.1" compose-navigation = "2.5.3" -compose-accompanist = "0.28.0" +compose-accompanist = "0.30.1" compose-paging = "1.0.0-alpha18" # endregion Compose diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ddde06c17d..135c32a8de 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Mar 07 14:45:01 MSK 2023 distributionBase = GRADLE_USER_HOME -distributionUrl = https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +distributionUrl = https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip distributionPath = wrapper/dists zipStorePath = wrapper/dists zipStoreBase = GRADLE_USER_HOME diff --git a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/ConfigurationPlugin.kt b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/ConfigurationPlugin.kt index 53b9d53e1c..26359f8f2d 100644 --- a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/ConfigurationPlugin.kt +++ b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/ConfigurationPlugin.kt @@ -12,12 +12,12 @@ import org.gradle.api.Project import org.gradle.kotlin.dsl.configure class ConfigurationPlugin : Plugin { - override fun apply(target: Project) { - target.plugins.all(startConfigurationAction(project = target)) + override fun apply(project: Project) { + project.configure() + project.plugins.all(startPluginConfigurationAction(project)) } - private fun startConfigurationAction(project: Project) = Action> { - project.configure() + private fun startPluginConfigurationAction(project: Project) = Action> { when (this) { is AppPlugin -> { project.configure { configure(project) } From 1a4e8d8f20452926dc2c91263066906ee46b0fa1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Jun 2023 08:53:24 +0300 Subject: [PATCH 2/4] Updated on 2026-08-14 --- .../java/com/tangem/tap/domain/TapErrors.kt | 9 +- .../tap/domain/model/WalletDataModel.kt | 1 + .../utils/WalletDataOperations.kt | 5 +- .../features/send/redux/SendScreenAction.kt | 2 +- .../send/redux/reducers/SendScreenReducer.kt | 2 +- .../tap/features/wallet/redux/WalletAction.kt | 2 +- .../middlewares/TradeCryptoMiddleware.kt | 10 +- .../middlewares/WalletDialogsMiddleware.kt | 2 + .../redux/middlewares/WalletMiddleware.kt | 201 ++++++++++-------- .../wallet/redux/models/WalletDialog.kt | 2 +- .../dialogs/AmountToSendBottomSheetDialog.kt | 8 +- 11 files changed, 142 insertions(+), 102 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index b0ee231286..fc7bdedd39 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -22,6 +22,10 @@ sealed class TapError( object UnknownError : TapError(R.string.send_error_unknown) open class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage)) + data class UnsupportedState( + val stateError: String, + ) : TapError(R.string.common_custom_string, listOf("Unsupported state: $stateError")) + object ScanCardError : TapError(R.string.scan_card_error) object UnknownBlockchain : TapError(R.string.wallet_error_unsupported_blockchain_subtitle) object NoInternetConnection : TapError(R.string.wallet_notification_no_internet) @@ -38,11 +42,6 @@ sealed class TapError( data class DustAmount(override val args: List) : TapError(R.string.send_error_dust_amount_format) object DustChange : TapError(R.string.send_error_dust_change) - data class UnsupportedState( - val stateError: String, - val customMessage: String = "Unsupported state:", - ) : TapError(R.string.common_custom_string, listOf("$customMessage $stateError")) - sealed class WalletManager { object CreationError : CustomError("Can't create wallet manager") class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount) diff --git a/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt b/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt index fe751acee5..e6782972eb 100644 --- a/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt +++ b/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt @@ -79,6 +79,7 @@ data class WalletDataModel( data class Unreachable( override val errorMessage: String?, + override val amount: BigDecimal = BigDecimal.ZERO, ) : Status() { override val isErrorStatus: Boolean = true } diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt index e342ff4e70..396329ad4f 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt @@ -170,7 +170,10 @@ internal fun List.updateWithMissedDerivation(): List.updateWithUnreachable(): List { return this.map { walletData -> walletData.copy( - status = WalletDataModel.Unreachable(errorMessage = null), + status = WalletDataModel.Unreachable( + errorMessage = null, + amount = walletData.status.amount, + ), ) } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 719cd930c3..5dee274734 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -26,9 +26,9 @@ interface SendScreenActionUi : SendScreenAction object ReleaseSendState : Action data class PrepareSendScreen( + val walletManager: WalletManager, val coinAmount: Amount?, val coinRate: BigDecimal?, - val walletManager: WalletManager?, val tokenAmount: Amount? = null, val tokenRate: BigDecimal? = null, ) : SendScreenAction diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index 48eafac40f..4134e8c148 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -85,7 +85,7 @@ private class EmptyReducer : SendInternalReducer { private class PrepareSendScreenStatesReducer : SendInternalReducer { override fun handle(action: SendScreenAction, sendState: SendState): SendState { val prepareAction = action as PrepareSendScreen - val walletManager = action.walletManager!! + val walletManager = action.walletManager val amountToExtract = prepareAction.tokenAmount ?: prepareAction.coinAmount!! val decimals = amountToExtract.decimals val feePaidInNetworkCurrency = isFeePaidInNetworkCurrency(walletManager.wallet.blockchain) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index db13376244..8db98a4ee0 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -98,7 +98,7 @@ sealed class WalletAction : Action { val swapAllowed: Boolean, ) : DialogAction() - data class ChooseCurrency(val amounts: List?) : DialogAction() + data class ChooseCurrency(val amounts: List) : DialogAction() data class RussianCardholdersWarningDialog( val dialogData: WalletDialog.RussianCardholdersWarningDialog.Data? = null, ) : DialogAction() diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 6020f4c151..0c56d30f51 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.wallet.redux.middlewares import androidx.core.os.bundleOf +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager import com.tangem.blockchain.common.AmountType import com.tangem.common.extensions.guard @@ -129,13 +130,16 @@ class TradeCryptoMiddleware { val selectedWalletData = store.state.walletState.selectedWalletData ?: return Analytics.send(Token.ButtonSend(AnalyticsParam.CurrencyType.Currency(selectedWalletData.currency))) + val walletManager = store.state.walletState.getWalletManager(selectedWalletData.currency).guard { + FirebaseCrashlytics.getInstance().recordException(IllegalStateException("WalletManager is null")) + return + } - val walletManager = store.state.walletState.getWalletManager(selectedWalletData.currency) store.dispatchOnMain( PrepareSendScreen( - coinAmount = walletManager?.wallet?.amounts?.get(AmountType.Coin), - coinRate = selectedWalletData.fiatRate, walletManager = walletManager, + coinAmount = walletManager.wallet.amounts[AmountType.Coin], + coinRate = selectedWalletData.fiatRate, ), ) store.dispatchOnMain( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletDialogsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletDialogsMiddleware.kt index c21a15ce98..b381ec0365 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletDialogsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletDialogsMiddleware.kt @@ -37,6 +37,8 @@ class WalletDialogsMiddleware { ) } is WalletAction.DialogAction.ChooseCurrency -> { + if (action.amounts.isEmpty()) return + store.dispatchDialogShow( WalletDialog.SelectAmountToSendDialog( amounts = action.amounts, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index b71a2eccc9..c7bde94141 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.wallet.redux.middlewares import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.address.AddressType import com.tangem.common.CompletionResult import com.tangem.common.doOnSuccess @@ -32,7 +33,6 @@ import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.utils.coroutines.ifActive -import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -147,26 +147,76 @@ class WalletMiddleware { store.dispatchOpenUrl(action.exploreUrl) } is WalletAction.Send -> { + val walletStore = walletState.getWalletStore(walletState.selectedCurrency) + val selectedWalletData = walletState.selectedWalletData + val walletManager = walletStore?.walletManager + + if (walletStore == null || walletManager == null || selectedWalletData == null) { + val error = TapError.UnsupportedState( + "WalletAction.Send: walletStore or selectedWalletData or walletManager is null", + ) + FirebaseCrashlytics.getInstance().recordException(IllegalStateException(error.stateError)) + store.dispatchErrorNotification(error) + return + } if (!networkConnectionManager.isOnline) { store.dispatchErrorNotification(TapError.NoInternetConnection) return } - val newAction = prepareSendAction(action.amount, store.state.walletState) - if (newAction is PrepareSendScreen && newAction.walletManager == null) { - store.dispatch(NavigationAction.PopBackTo(screen = AppScreen.Home)) - FirebaseCrashlytics.getInstance().recordException( - IllegalStateException("PrepareSendScreen: walletManager is null"), - ) - store.dispatchToastNotification(R.string.internal_error_wallet_manager_not_found) - } else { - store.dispatch(newAction) - if (newAction is PrepareSendScreen) { - store.state.walletState.selectedWalletData?.currency?.let { currency -> - Analytics.send(Token.ButtonSend(AnalyticsParam.CurrencyType.Currency(currency))) - } - store.dispatch(NavigationAction.NavigateTo(AppScreen.Send)) + + val currency = selectedWalletData.currency + val initSendStateAction = if (action.amount == null) { + val sendableAmounts = walletManager.wallet.getSendableAmounts() + if (sendableAmounts.isEmpty()) { + val error = TapError.UnsupportedState("WalletAction.Send: Nothing to send") + FirebaseCrashlytics.getInstance().recordException(IllegalStateException(error.stateError)) + store.dispatchErrorNotification(error) + return } + + if (walletState.isMultiwalletAllowed) { + val amountToSend = sendableAmounts.find { it.currencySymbol == currency.currencySymbol } + if (amountToSend == null) { + val error = TapError.UnsupportedState("WalletAction.Send: Amount to send is null") + FirebaseCrashlytics.getInstance().recordException(IllegalStateException(error.stateError)) + store.dispatchErrorNotification(error) + return + } + + makeInitSendStateActionByCurrency( + currency = currency, + amount = amountToSend, + walletStore = walletStore, + walletManager = walletManager, + selectedWalletData = selectedWalletData, + ) + } else { + val isSingleAmount = sendableAmounts.size == 1 + if (isSingleAmount) { + makeInitSendStateActionByAmount( + amount = sendableAmounts.first(), + walletStore = walletStore, + walletManager = walletManager, + selectedWalletData = selectedWalletData, + ) + } else { + store.dispatch(WalletAction.DialogAction.ChooseCurrency(sendableAmounts)) + return + } + } + } else { + // action.amount received from the ChooseCurrency dialog + makeInitSendStateActionByAmount( + amount = action.amount, + walletManager = walletManager, + walletStore = walletStore, + selectedWalletData = selectedWalletData, + ) } + + Analytics.send(Token.ButtonSend(AnalyticsParam.CurrencyType.Currency(currency))) + store.dispatch(initSendStateAction) + store.dispatch(NavigationAction.NavigateTo(AppScreen.Send)) } is WalletAction.ShowSaveWalletIfNeeded -> { showSaveWalletIfNeeded() @@ -222,6 +272,59 @@ class WalletMiddleware { } } + private fun makeInitSendStateActionByAmount( + amount: Amount, + walletStore: WalletStoreModel, + walletManager: WalletManager, + selectedWalletData: WalletDataModel, + ): PrepareSendScreen = when (amount.type) { + AmountType.Coin -> + PrepareSendScreen( + walletManager = walletManager, + coinAmount = amount, + coinRate = selectedWalletData.fiatRate, + ) + is AmountType.Token -> { + PrepareSendScreen( + walletManager = walletManager, + coinAmount = walletManager.wallet.amounts[AmountType.Coin], + coinRate = walletStore.blockchainWalletData.fiatRate, + tokenAmount = amount, + tokenRate = selectedWalletData.fiatRate, + ) + } + AmountType.Reserve -> { + val exception = IllegalStateException("WalletAction.Send: Reserve can't be sent") + FirebaseCrashlytics.getInstance().recordException(exception) + throw exception + } + } + + private fun makeInitSendStateActionByCurrency( + currency: Currency, + amount: Amount, + walletStore: WalletStoreModel, + walletManager: WalletManager, + selectedWalletData: WalletDataModel, + ): PrepareSendScreen = when (currency) { + is Currency.Blockchain -> { + PrepareSendScreen( + walletManager = walletManager, + coinAmount = amount, + coinRate = selectedWalletData.fiatRate, + ) + } + is Currency.Token -> { + PrepareSendScreen( + walletManager = walletManager, + coinAmount = walletManager.wallet.amounts[AmountType.Coin], + coinRate = walletStore.blockchainWalletData.fiatRate, + tokenAmount = amount, + tokenRate = selectedWalletData.fiatRate, + ) + } + } + private fun changeSelectedWalletAddress(type: AddressType, state: WalletState) { val selectedUserWalletId = userWalletsListManager.selectedUserWalletSync?.walletId.guard { Timber.e("Unable to change selected wallet address, no user wallet selected") @@ -284,72 +387,4 @@ class WalletMiddleware { } } } - - private fun prepareSendAction(amount: Amount?, state: WalletState?): Action { - val selectedWalletData = state?.selectedWalletData - val currency = selectedWalletData?.currency - val walletStore = state?.getWalletStore(currency) - - return if (amount != null) { - if (amount.type is AmountType.Token) { - prepareSendActionForToken(amount, selectedWalletData, walletStore) - } else { - PrepareSendScreen(amount, selectedWalletData?.fiatRate, walletStore?.walletManager) - } - } else { - val amounts = walletStore?.walletManager?.wallet?.getSendableAmounts() - if (currency != null && state.isMultiwalletAllowed) { - when (currency) { - is Currency.Blockchain -> { - val amountToSend = amounts?.find { it.currencySymbol == currency.blockchain.currency } - ?: return WalletAction.DialogAction.ChooseCurrency(amounts) - PrepareSendScreen( - coinAmount = amountToSend, - coinRate = selectedWalletData.fiatRate, - walletManager = walletStore.walletManager, - ) - } - is Currency.Token -> { - val amountToSend = amounts?.find { it.currencySymbol == currency.token.symbol } - ?: return WalletAction.DialogAction.ChooseCurrency(amounts) - prepareSendActionForToken( - amount = amountToSend, - selectedWalletData = selectedWalletData, - walletStore = walletStore, - ) - } - } - } else { - val amountsSize = amounts?.size ?: 0 - if (amountsSize > 1) { - WalletAction.DialogAction.ChooseCurrency(amounts) - } else { - val amountToSend = amounts?.first() - PrepareSendScreen( - coinAmount = amountToSend, - coinRate = selectedWalletData?.fiatRate, - walletManager = walletStore?.walletManager, - ) - } - } - } - } - - private fun prepareSendActionForToken( - amount: Amount, - selectedWalletData: WalletDataModel?, - walletStore: WalletStoreModel?, - ): PrepareSendScreen { - val coinRate = walletStore?.blockchainWalletData?.fiatRate - val tokenRate = selectedWalletData?.fiatRate - val coinAmount = walletStore?.walletManager?.wallet?.amounts?.get(AmountType.Coin) - - return PrepareSendScreen( - coinAmount = coinAmount, - coinRate = coinRate, - walletManager = walletStore?.walletManager, - tokenAmount = amount, - tokenRate = tokenRate, - ) - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt index c772684c48..88fbb86932 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt @@ -6,7 +6,7 @@ import com.tangem.tap.common.redux.StateDialog import com.tangem.wallet.R sealed interface WalletDialog : StateDialog { - data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog + data class SelectAmountToSendDialog(val amounts: List) : WalletDialog object SignedHashesMultiWalletDialog : WalletDialog data class ChooseTradeActionDialog( val buyAllowed: Boolean, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/AmountToSendBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/AmountToSendBottomSheetDialog.kt index 160d3fc40e..75cbe3010a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/AmountToSendBottomSheetDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/AmountToSendBottomSheetDialog.kt @@ -5,11 +5,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import androidx.appcompat.view.ContextThemeWrapper -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.* import com.google.android.material.bottomsheet.BottomSheetDialog import com.tangem.blockchain.common.Amount import com.tangem.tap.common.extensions.toFormattedString @@ -55,7 +51,7 @@ class AmountToSendBottomSheetDialog( } } -class ChooseAmountAdapter : ListAdapter(DiffUtilCallback) { +private class ChooseAmountAdapter : ListAdapter(DiffUtilCallback) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AmountViewHolder { val binding = ItemWalletAmountToSendBinding.inflate( From 17f89eebd5cd6b54ff19b1c847665a563341ab38 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Jun 2023 14:09:21 +0300 Subject: [PATCH 3/4] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 1 + .../com/tangem/core/ui/components/Buttons.kt | 186 ++++++-- .../com/tangem/core/ui/components/Shimmers.kt | 4 +- .../main/res/drawable/ic_group_drop_24.xml | 10 + features/wallet/impl/build.gradle.kts | 1 + .../presentation/common/WalletPreviewData.kt | 161 +++++++ .../common/component/NetworkGroupItem.kt | 79 ++++ .../common/component/TokenItem.kt | 412 ++++++++++++++++++ .../common/state/TokenItemState.kt | 111 +++++ .../common/state/TokenListState.kt | 35 ++ .../router/DefaultWalletRouter.kt | 4 +- .../presentation/router/InnerWalletRouter.kt | 5 + .../wallet/presentation/state/TokenUIState.kt | 9 - .../presentation/state/TokenV2Config.kt | 39 -- .../feature/wallet/presentation/ui/Tokens.kt | 352 --------------- .../presentation/ui/WalletPreviewData.kt | 51 --- .../{ => wallet}/state/WalletCardState.kt | 2 +- .../{ => wallet}/state/WalletStateHolder.kt | 6 +- .../{ => wallet}/ui/WalletScreen.kt | 6 +- .../{ => wallet}/ui/components/WalletCard.kt | 10 +- .../ui/components/WalletHeader.kt | 11 +- .../viewmodels/WalletViewModel.kt | 6 +- 23 files changed, 1001 insertions(+), 501 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_group_drop_24.xml create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenUIState.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenV2Config.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/{ => wallet}/state/WalletCardState.kt (97%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/{ => wallet}/state/WalletStateHolder.kt (79%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/{ => wallet}/ui/WalletScreen.kt (69%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/{ => wallet}/ui/components/WalletCard.kt (95%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/{ => wallet}/ui/components/WalletHeader.kt (91%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/{ => wallet}/viewmodels/WalletViewModel.kt (83%) diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index af94ad33ae..245601c136 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -471,6 +471,7 @@ Аккаунт не создан Эта карта не поддерживается Ваша карта Tangem предназначена для работы с другим приложением. Пожалуйста, ознакомьтесь с названием и инструкциями на вашей карте и установите правильное приложение. + Сеть %s Адрес скопирован в буфер обмена Нет соединения с интернетом Получение diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 038f2a7fe9..3874bb14cb 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -463,6 +463,7 @@ Account is not created This card is not supported Your Tangem card was made to work with a different application. Please see the name and instructions on your card, and install the correct app + %s network Address was copied to clipboard No internet connection Receiving 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 50651d2e35..8e63ac74d1 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 @@ -1,29 +1,9 @@ package com.tangem.core.ui.components 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.IntrinsicSize -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentSize -import androidx.compose.material.Button -import androidx.compose.material.ButtonColors -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.ButtonElevation -import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.Divider -import androidx.compose.material.Icon -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable -import androidx.compose.runtime.State -import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.foundation.layout.* +import androidx.compose.material.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -244,6 +224,78 @@ fun SelectorButton(text: String, onClick: () -> Unit, modifier: Modifier = Modif } // endregion Other +// region Action + +/** + * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=290-305&t=3z98eFnTeyIx5TH5-4) + * */ +@Composable +fun RoundedActionButton( + text: String, + icon: Painter, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, +) { + TangemButton( + modifier = modifier, + text = text, + icon = TangemButtonIcon.Left(icon), + onClick = onClick, + enabled = enabled, + showProgress = false, + colors = TangemButtonsDefaults.secondaryButtonColors, + size = TangemButtonSize.RoundedAction, + ) +} + +/** + * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=1208-1395&t=3z98eFnTeyIx5TH5-4) + * */ +@Composable +fun ActionButton( + text: String, + icon: Painter, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, +) { + TangemButton( + modifier = modifier, + text = text, + icon = TangemButtonIcon.Left(icon), + onClick = onClick, + enabled = enabled, + showProgress = false, + colors = TangemButtonsDefaults.secondaryButtonColors, + size = TangemButtonSize.Action, + ) +} + +/** + * Same as [RoundedActionButton] but colored in primary background color + * */ +@Composable +fun BackgroundActionButton( + text: String, + icon: Painter, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, +) { + TangemButton( + modifier = modifier, + text = text, + icon = TangemButtonIcon.Left(icon), + onClick = onClick, + enabled = enabled, + showProgress = false, + colors = TangemButtonsDefaults.backgroundButtonColors, + size = TangemButtonSize.RoundedAction, + ) +} +// endregion Action + // region Defaults @Suppress("LongParameterList") @Composable @@ -335,13 +387,12 @@ private fun ButtonContent( } } -sealed interface TangemButtonIcon { +@Immutable +private sealed interface TangemButtonIcon { val painter: Painter? - @Immutable data class Left(override val painter: Painter) : TangemButtonIcon - @Immutable data class Right(override val painter: Painter) : TangemButtonIcon object None : TangemButtonIcon { @@ -349,10 +400,12 @@ sealed interface TangemButtonIcon { } } -enum class TangemButtonSize { +private enum class TangemButtonSize { Default, Text, Selector, + Action, + RoundedAction, } @Composable @@ -360,6 +413,9 @@ private fun TangemButtonSize.toHeightDp(): Dp = when (this) { TangemButtonSize.Default -> TangemTheme.dimens.size48 TangemButtonSize.Text -> TangemTheme.dimens.size40 TangemButtonSize.Selector -> TangemTheme.dimens.size24 + TangemButtonSize.Action, + TangemButtonSize.RoundedAction, + -> TangemTheme.dimens.size36 } @Composable @@ -367,6 +423,8 @@ private fun TangemButtonSize.toShape(): Shape = when (this) { TangemButtonSize.Default -> TangemTheme.shapes.roundedCornersMedium TangemButtonSize.Text -> TangemTheme.shapes.roundedCornersSmall TangemButtonSize.Selector -> TangemTheme.shapes.roundedCornersSmall + TangemButtonSize.Action -> TangemTheme.shapes.roundedCornersMedium + TangemButtonSize.RoundedAction -> TangemTheme.shapes.roundedCornersLarge } @Composable @@ -374,6 +432,9 @@ private fun TangemButtonSize.toIconPadding(): Dp = when (this) { TangemButtonSize.Default -> TangemTheme.dimens.spacing8 TangemButtonSize.Text -> TangemTheme.dimens.spacing8 TangemButtonSize.Selector -> 0.dp + TangemButtonSize.Action, + TangemButtonSize.RoundedAction, + -> TangemTheme.dimens.spacing8 } object TangemButtonsDefaults { @@ -423,6 +484,14 @@ object TangemButtonsDefaults { disabledBackgroundColor = Color.Transparent, disabledContentColor = TangemTheme.colors.text.disabled, ) + + val backgroundButtonColors: ButtonColors + @Composable get() = TangemButtonColors( + backgroundColor = TangemTheme.colors.background.primary, + contentColor = TangemTheme.colors.text.primary1, + disabledBackgroundColor = TangemTheme.colors.button.disabled, + disabledContentColor = TangemTheme.colors.text.disabled, + ) } @Immutable @@ -650,4 +719,67 @@ private fun TextButtonPreview_Dark() { TextButtonSample() } } + +@Composable +private fun ActionButtonSample(modifier: Modifier = Modifier) { + Column( + modifier = modifier + .background(TangemTheme.colors.background.primary), + ) { + RoundedActionButton( + text = "Send", + icon = painterResource(id = R.drawable.ic_arrow_up_24), + onClick = { /* [REDACTED_TODO_COMMENT]*/ }, + ) + Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) + ActionButton( + text = "Send", + icon = painterResource(id = R.drawable.ic_arrow_up_24), + onClick = { /* [REDACTED_TODO_COMMENT]*/ }, + ) + Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) + BackgroundActionButton( + text = "Send", + icon = painterResource(id = R.drawable.ic_arrow_up_24), + onClick = { /* [REDACTED_TODO_COMMENT]*/ }, + ) + Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) + RoundedActionButton( + text = "Send", + icon = painterResource(id = R.drawable.ic_arrow_up_24), + enabled = false, + onClick = { /* [REDACTED_TODO_COMMENT]*/ }, + ) + Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) + ActionButton( + text = "Send", + icon = painterResource(id = R.drawable.ic_arrow_up_24), + enabled = false, + onClick = { /* [REDACTED_TODO_COMMENT]*/ }, + ) + Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) + BackgroundActionButton( + text = "Send", + icon = painterResource(id = R.drawable.ic_arrow_up_24), + enabled = false, + onClick = { /* [REDACTED_TODO_COMMENT]*/ }, + ) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun ActionButtonPreview_Light() { + TangemTheme { + ActionButtonSample() + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun ActionButtonPreview_Dark() { + TangemTheme(isDark = true) { + ActionButtonSample() + } +} // endregion Preview \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt index 3b6227abbc..e793e21eaf 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt @@ -14,7 +14,7 @@ import com.valentinilk.shimmer.shimmer * Rectangle shimmer item with rounded shape from DS */ @Composable -fun ShimmerRectangle(modifier: Modifier = Modifier) { +fun RectangleShimmer(modifier: Modifier = Modifier) { Box( modifier = modifier .shimmer() @@ -51,7 +51,7 @@ private fun ShimmersPreview() { modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18), ) { - ShimmerRectangle( + RectangleShimmer( modifier = Modifier.size( width = TangemTheme.dimens.size72, height = TangemTheme.dimens.size12, diff --git a/core/ui/src/main/res/drawable/ic_group_drop_24.xml b/core/ui/src/main/res/drawable/ic_group_drop_24.xml new file mode 100644 index 0000000000..6e8c84c533 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_group_drop_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index c0a25c65df..183e43c174 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { implementation(deps.compose.ui.tooling) implementation(deps.compose.shimmer) implementation(deps.compose.accompanist.systemUiController) + implementation(deps.kotlin.immutable.collections) /** DI */ implementation(deps.hilt.android) 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 new file mode 100644 index 0000000000..619f57d8bb --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -0,0 +1,161 @@ +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.wallet.state.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import kotlinx.collections.immutable.persistentListOf +import java.util.UUID + +internal object WalletPreviewData { + + val walletCardContent = WalletCardState.Content( + id = UUID.randomUUID().toString(), + title = "Wallet 1", + balance = "8923,05 $", + additionalInfo = "3 cards • Seed enabled", + imageResId = R.drawable.ill_businessman_3d, + onClick = {}, + ) + + val walletCardLoading = WalletCardState.Loading( + id = UUID.randomUUID().toString(), + title = "Wallet 1", + additionalInfo = "3 cards • Seed enabled", + imageResId = R.drawable.ill_businessman_3d, + onClick = {}, + ) + + val walletCardHiddenContent = WalletCardState.HiddenContent( + id = UUID.randomUUID().toString(), + title = "Wallet 1", + additionalInfo = "3 cards • Seed enabled", + imageResId = R.drawable.ill_businessman_3d, + onClick = {}, + ) + + val walletCardError = WalletCardState.Error( + id = UUID.randomUUID().toString(), + title = "Wallet 1", + additionalInfo = "3 cards • Seed enabled", + imageResId = R.drawable.ill_businessman_3d, + 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, + tokenIconResId = R.drawable.img_polygon_22, + networkIconResId = R.drawable.img_polygon_22, + name = "Polygon", + amount = "5,412 MATIC", + hasPending = true, + tokenOptions = TokenOptionsState.Visible( + fiatAmount = "321 $", + priceChange = TokenOptionsState.PriceChange( + valuePercent = "2%", + type = TokenOptionsState.PriceChange.Type.UP, + ), + ), + ) + + val tokenItemHiddenState = TokenItemState.Content( + id = UUID.randomUUID().toString(), + tokenIconUrl = null, + tokenIconResId = R.drawable.img_polygon_22, + networkIconResId = R.drawable.img_polygon_22, + name = "Polygon", + amount = "5,412 MATIC", + hasPending = true, + tokenOptions = TokenOptionsState.Hidden( + priceChange = TokenOptionsState.PriceChange( + valuePercent = "2%", + type = TokenOptionsState.PriceChange.Type.UP, + ), + ), + ) + + val tokenItemDragState = TokenItemState.Draggable( + id = UUID.randomUUID().toString(), + tokenIconUrl = null, + tokenIconResId = R.drawable.img_polygon_22, + networkIconResId = R.drawable.img_polygon_22, + name = "Polygon", + fiatAmount = "3 172,14 $", + ) + + val tokenItemUnreachableState = TokenItemState.Unreachable( + id = UUID.randomUUID().toString(), + tokenIconUrl = null, + tokenIconResId = R.drawable.img_polygon_22, + networkIconResId = R.drawable.img_polygon_22, + name = "Polygon", + ) + + val loadingTokenItemState = TokenItemState.Loading(id = UUID.randomUUID().toString()) + + 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 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", + ), + tokenItemVisibleState.copy( + id = "token_2", + networkIconResId = R.drawable.img_eth_22, + amount = "733,71097 MATIC", + ), + tokenItemVisibleState.copy( + id = "token_3", + name = "USDT", + tokenIconResId = R.drawable.img_arbitrum_22, + networkIconResId = R.drawable.img_eth_22, + amount = "0,25404523 ARB", + ), + ), + ) + + val draggableNetworkGroup = NetworkGroupState.Draggable( + id = UUID.randomUUID().toString(), + networkName = "Ethereum", + tokens = draggableTokenList, + ) +} \ 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 new file mode 100644 index 0000000000..ee2d924e85 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt @@ -0,0 +1,79 @@ +package com.tangem.feature.wallet.presentation.common.component + +import androidx.compose.foundation.background +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 +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.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) { + Column(modifier = modifier) { + Row( + modifier = Modifier + .background(TangemTheme.colors.background.primary) + .padding(horizontal = TangemTheme.dimens.spacing12) + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size48), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + text = stringResource(id = R.string.wallet_network_group_title, state.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) + } + } + } + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun NetworkGroupItemPreview_Light(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) { + TangemTheme { + NetworkGroupItem(group) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun NetworkGroupItemPreview_Dark(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) { + TangemTheme(isDark = true) { + NetworkGroupItem(group) + } +} + +private class NetworkGroupProvider : CollectionPreviewParameterProvider( + collection = listOf( + WalletPreviewData.networkGroup, + WalletPreviewData.draggableNetworkGroup, + ), +) +// 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 new file mode 100644 index 0000000000..78c5b5f322 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt @@ -0,0 +1,412 @@ +package com.tangem.feature.wallet.presentation.common.component + +import androidx.annotation.DrawableRes +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.runtime.Stable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +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.constraintlayout.compose.ConstrainedLayoutReference +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintLayoutScope +import androidx.constraintlayout.compose.Dimension +import coil.compose.SubcomposeAsyncImage +import coil.request.ImageRequest +import com.tangem.core.ui.components.* +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemTypography +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.common.WalletPreviewData +import com.tangem.feature.wallet.presentation.common.state.TokenItemState +import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState + +private const val DOTS = "•••" + +@Composable +internal fun TokenItem(state: TokenItemState, modifier: Modifier = Modifier) { + when (state) { + is TokenItemState.Content -> ContentTokenItem(state, modifier) + is TokenItemState.Loading -> LoadingTokenItem(modifier) + is TokenItemState.Draggable -> DraggableTokenItem(state, modifier) + is TokenItemState.Unreachable -> UnreachableTokenItem(state, modifier) + } +} + +@Composable +private fun ContentTokenItem(content: TokenItemState.Content, modifier: Modifier = Modifier) { + InternalTokenItem( + modifier = modifier, + name = content.name, + tokenIconUrl = content.tokenIconUrl, + tokenIconResId = content.tokenIconResId, + networkIconResId = content.networkIconResId, + amount = if (content.tokenOptions is TokenOptionsState.Hidden) DOTS else content.amount, + hasPending = content.hasPending, + options = { ref -> + TokenOptionsBlock( + modifier = Modifier.constrainAsOptionsItem(scope = this, ref), + state = content.tokenOptions, + ) + }, + ) +} + +@Composable +internal fun DraggableTokenItem(state: TokenItemState.Draggable, modifier: Modifier = Modifier) { + InternalTokenItem( + modifier = modifier, + name = state.name, + tokenIconUrl = state.tokenIconUrl, + tokenIconResId = state.tokenIconResId, + networkIconResId = state.networkIconResId, + amount = state.fiatAmount, + hasPending = false, + options = { ref -> + Icon( + modifier = Modifier.constrainAsOptionsItem(scope = this, ref), + painter = painterResource(id = R.drawable.ic_drag_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, + ) + }, + ) +} + +@Composable +internal fun UnreachableTokenItem(state: TokenItemState.Unreachable, modifier: Modifier = Modifier) { + InternalTokenItem( + modifier = modifier, + name = state.name, + tokenIconUrl = state.tokenIconUrl, + tokenIconResId = state.tokenIconResId, + networkIconResId = state.networkIconResId, + amount = null, + hasPending = false, + options = { ref -> + Text( + modifier = Modifier.constrainAsOptionsItem(scope = this, ref), + text = "Unreachable", // TODO (conform this text) + style = TangemTypography.body2, + color = TangemTheme.colors.text.tertiary, + ) + }, + ) +} + +@Composable +private fun LoadingTokenItem(modifier: Modifier = Modifier) { + BaseSurface(modifier) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = TangemTheme.dimens.spacing12, + vertical = TangemTheme.dimens.spacing4, + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), + ) { + CircleShimmer(modifier = Modifier.size(size = TangemTheme.dimens.size42)) + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { + RectangleShimmer( + modifier = Modifier.size( + width = TangemTheme.dimens.size72, + height = TangemTheme.dimens.size12, + ), + ) + RectangleShimmer( + modifier = Modifier.size( + width = TangemTheme.dimens.size50, + height = TangemTheme.dimens.size12, + ), + ) + } + Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { + RectangleShimmer( + modifier = Modifier.size( + width = TangemTheme.dimens.size40, + height = TangemTheme.dimens.size12, + ), + ) + RectangleShimmer( + modifier = Modifier.size( + width = TangemTheme.dimens.size40, + height = TangemTheme.dimens.size12, + ), + ) + } + } + } + } +} + +/** + * Block for end part of token item + * shows status is reachable, is drag, hidden or show balance + */ +@Composable +private fun TokenOptionsBlock(state: TokenOptionsState, modifier: Modifier = Modifier) { + when (state) { + is TokenOptionsState.Visible -> { + TokenFiatPercentageBlock( + modifier = modifier, + fiatAmount = state.fiatAmount, + priceChange = state.priceChange, + ) + } + is TokenOptionsState.Hidden -> { + TokenFiatPercentageBlock( + modifier = modifier, + fiatAmount = DOTS, + priceChange = state.priceChange, + ) + } + } +} + +@Suppress("LongParameterList") +@Composable +private fun InternalTokenItem( + name: String, + tokenIconUrl: String?, + @DrawableRes tokenIconResId: Int, + @DrawableRes networkIconResId: Int?, + amount: String?, + hasPending: Boolean, + options: @Composable ConstraintLayoutScope.(ref: ConstrainedLayoutReference) -> Unit, + modifier: Modifier = Modifier, +) { + BaseSurface(modifier) { + ConstraintLayout( + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = TangemTheme.dimens.spacing12, + vertical = TangemTheme.dimens.spacing4, + ), + ) { + val (iconItem, tokenNameItem, optionsItem) = createRefs() + + TokenIcon( + modifier = Modifier.constrainAs(iconItem) { + centerVerticallyTo(parent) + start.linkTo(parent.start) + }, + tokenIconUrl = tokenIconUrl, + tokenIconResId = tokenIconResId, + networkIconRes = networkIconResId, + ) + + TokenTitleAmountBlock( + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing12) + .constrainAs(tokenNameItem) { + centerVerticallyTo(parent) + start.linkTo(iconItem.end) + end.linkTo(optionsItem.start) + width = Dimension.fillToConstraints + }, + title = name, + amount = amount, + hasPending = hasPending, + ) + + options(optionsItem) + } + } +} + +@Stable +private fun Modifier.constrainAsOptionsItem(scope: ConstraintLayoutScope, ref: ConstrainedLayoutReference): Modifier { + return with(scope) { + this@constrainAsOptionsItem.constrainAs(ref) { + centerVerticallyTo(parent) + end.linkTo(parent.end) + } + } +} + +@Composable +private fun BaseSurface( + modifier: Modifier = Modifier, + onClick: (() -> Unit)? = null, + content: @Composable () -> Unit, +) { + Surface( + modifier = modifier.defaultMinSize(minHeight = TangemTheme.dimens.size68), + color = TangemTheme.colors.background.primary, + onClick = onClick ?: {}, + enabled = onClick != null, + ) { + content() + } +} + +@Composable +private fun TokenTitleAmountBlock(title: String, amount: String?, hasPending: Boolean, modifier: Modifier = Modifier) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2), + ) { + Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { + Text( + text = title, + style = TangemTypography.subtitle2, + color = TangemTheme.colors.text.primary1, + ) + if (hasPending) { + Image( + modifier = Modifier.align(Alignment.CenterVertically), + painter = painterResource(id = R.drawable.img_loader_15), + contentDescription = null, + ) + } + } + if (!amount.isNullOrBlank()) { + Text( + text = amount, + style = TangemTypography.body2, + color = TangemTheme.colors.text.tertiary, + ) + } + } +} + +@Composable +private fun TokenFiatPercentageBlock( + fiatAmount: String, + priceChange: TokenOptionsState.PriceChange, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier.requiredWidth(IntrinsicSize.Max)) { + Text( + modifier = Modifier.align(Alignment.End), + text = fiatAmount, + style = TangemTypography.body2, + color = TangemTheme.colors.text.primary1, + ) + SpacerH2() + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End, + ) { + val iconChangeArrow: Int + val changeTextColor: Color + when (priceChange.type) { + TokenOptionsState.PriceChange.Type.UP -> { + iconChangeArrow = R.drawable.img_arrow_up_8 + changeTextColor = TangemTheme.colors.text.accent + } + TokenOptionsState.PriceChange.Type.DOWN -> { + iconChangeArrow = R.drawable.img_arrow_down_8 + changeTextColor = TangemTheme.colors.text.warning + } + } + Image( + modifier = Modifier.align(Alignment.CenterVertically), + painter = painterResource(id = iconChangeArrow), + contentDescription = null, + ) + SpacerW4() + Text( + modifier = Modifier.align(Alignment.CenterVertically), + text = priceChange.valuePercent, + style = TangemTypography.body2, + color = changeTextColor, + ) + } + } +} + +@Composable +private fun TokenIcon( + tokenIconUrl: String?, + modifier: Modifier = Modifier, + @DrawableRes tokenIconResId: Int? = null, + @DrawableRes networkIconRes: Int? = null, +) { + Box( + modifier = modifier + .padding(end = TangemTheme.dimens.spacing16) + .size(TangemTheme.dimens.size42), + ) { + val tokenImageModifier = Modifier + .align(Alignment.BottomStart) + .size(TangemTheme.dimens.size36) + + val data = if (tokenIconUrl.isNullOrEmpty()) tokenIconResId else tokenIconUrl + SubcomposeAsyncImage( + modifier = tokenImageModifier, + model = ImageRequest.Builder(LocalContext.current) + .data(data) + .crossfade(true) + .build(), + loading = { CircleShimmer(modifier = tokenImageModifier) }, + contentDescription = null, + ) + + if (networkIconRes != null) { + Box( + modifier = Modifier + .align(Alignment.TopEnd) + .size(TangemTheme.dimens.size18) + .background(color = Color.White, shape = CircleShape), + contentAlignment = Alignment.Center, + ) { + Image( + modifier = Modifier.padding(all = 0.5.dp), + painter = painterResource(id = networkIconRes), + contentDescription = null, + ) + } + } + } +} + +// region preview + +@Preview +@Composable +private fun Preview_Tokens_LightTheme(@PreviewParameter(TokenConfigProvider::class) state: TokenItemState) { + TangemTheme(isDark = false) { + TokenItem(state) + } +} + +@Preview +@Composable +private fun Preview_Tokens_DarkTheme(@PreviewParameter(TokenConfigProvider::class) state: TokenItemState) { + TangemTheme(isDark = true) { + TokenItem(state) + } +} + +private class TokenConfigProvider : CollectionPreviewParameterProvider( + collection = listOf( + WalletPreviewData.tokenItemVisibleState, + WalletPreviewData.tokenItemUnreachableState, + WalletPreviewData.tokenItemDragState, + WalletPreviewData.tokenItemHiddenState, + WalletPreviewData.loadingTokenItemState, + ), +) + +// endregion preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt new file mode 100644 index 0000000000..706387e9d6 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt @@ -0,0 +1,111 @@ +package com.tangem.feature.wallet.presentation.common.state + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Immutable +import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState.PriceChange.Type + +/** Token item state */ +@Immutable +internal sealed interface TokenItemState { + + /** Token id */ + val id: String + + /** + * Loading token state + * + * @property id token id + */ + data class Loading(override val id: String) : TokenItemState + + /** + * Content token state + * + * @property id token id + * @property tokenIconUrl token icon url + * @property tokenIconResId token icon resource id + * @property networkIconResId network icon resource id, may be null if it is a coin + * @property name token name + * @property amount amount of token + * @property hasPending pending tx in blockchain + * @property tokenOptions state for token options + */ + data class Content( + override val id: String, + val tokenIconUrl: String?, + @DrawableRes val tokenIconResId: Int, + @DrawableRes val networkIconResId: Int?, + val name: String, + val amount: String, + val hasPending: Boolean, + val tokenOptions: TokenOptionsState, + ) : TokenItemState + + /** + * Draggable token state + * + * @property id token id + * @property tokenIconUrl token icon url + * @property tokenIconResId token icon resource id + * @property networkIconResId network icon resource id, may be null if it is a coin + * @property name token name + */ + data class Draggable( + override val id: String, + val tokenIconUrl: String?, + @DrawableRes val tokenIconResId: Int, + @DrawableRes val networkIconResId: Int?, + val name: String, + val fiatAmount: String, + ) : TokenItemState + + /** + * Unreachable token state + * + * @property id token id + * @property tokenIconUrl token icon url + * @property tokenIconResId token icon resource id + * @property networkIconResId network icon resource id, may be null if it is a coin + * @property name token name + */ + data class Unreachable( + override val id: String, + val tokenIconUrl: String?, + @DrawableRes val tokenIconResId: Int, + @DrawableRes val networkIconResId: Int?, + val name: String, + ) : TokenItemState + + /** Token options state */ + sealed interface TokenOptionsState { + + /** + * Visible token options state + * + * @property fiatAmount fiat amount of token + * @property priceChange value of price changing + */ + data class Visible(val fiatAmount: String, val priceChange: PriceChange) : TokenOptionsState + + /** + * Hidden token options state + * + * @property priceChange value of price changing + */ + data class Hidden(val priceChange: PriceChange) : TokenOptionsState + + /** + * Price changing option state + * + * @property valuePercent value in percent + * @property type type [Type] + */ + data class PriceChange(val valuePercent: String, val type: Type) { + + /** Price changing type */ + enum class Type { + UP, DOWN + } + } + } +} \ 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 new file mode 100644 index 0000000000..c504d1c81e --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt @@ -0,0 +1,35 @@ +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 +} + +@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/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 90a84bef1c..74751d0c6b 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,8 +10,8 @@ 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.ui.WalletScreen -import com.tangem.feature.wallet.presentation.viewmodels.WalletViewModel +import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen +import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel import kotlin.properties.Delegates /** Default implementation of wallet feature router */ diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 00782aa81d..7558ac61c7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -1,13 +1,18 @@ package com.tangem.feature.wallet.presentation.router import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import com.tangem.features.wallet.navigation.WalletRouter /** * Interface of inner wallet feature router * + * Annotated as [Stable] because implementation of this interface passed as argument to [Initialize] method by compose + * compiler + * [REDACTED_AUTHOR] */ +@Stable internal interface InnerWalletRouter : WalletRouter { /** Initialize router */ diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenUIState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenUIState.kt deleted file mode 100644 index ace321b30a..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenUIState.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.feature.wallet.presentation.state - -internal enum class TokenUIState { - LOADED, LOADING -} - -internal enum class TokenOptionsUIState { - VISIBLE, HIDDEN, DRAG, UNREACHABLE -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenV2Config.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenV2Config.kt deleted file mode 100644 index dca7440c39..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenV2Config.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tangem.feature.wallet.presentation.state - -import androidx.annotation.DrawableRes - -/** - * Token config - * - * @property name of token/coin - * @property amount of token - * @property fiatAmount amount in fiat - * @property priceChange value of price changing - * @property iconUrl - * @property icon token/coin icon - * @property networkIcon - * @property tokenUIState token state [TokenUIState] loading etc. - * @property tokenOptionsUIState state for token options like 'unreachable', 'hidden' etc - * @property hasPending pending tx in blockchain - */ -internal data class TokenV2Config( - val name: String, - val amount: String, - val fiatAmount: String, - val priceChange: PriceChange, - val iconUrl: String?, - @DrawableRes val icon: Int?, - @DrawableRes val networkIcon: Int, - val tokenUIState: TokenUIState, - val tokenOptionsUIState: TokenOptionsUIState, - val hasPending: Boolean, -) - -data class PriceChange( - val valuePercent: String, - val type: PriceChangeType, -) - -enum class PriceChangeType { - UP, DOWN -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt deleted file mode 100644 index 45b3eb843d..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt +++ /dev/null @@ -1,352 +0,0 @@ -package com.tangem.feature.wallet.presentation.ui - -import androidx.annotation.DrawableRes -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.graphics.Color -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.constraintlayout.compose.ConstraintLayout -import androidx.constraintlayout.compose.Dimension -import coil.compose.SubcomposeAsyncImage -import coil.request.ImageRequest -import com.tangem.core.ui.components.* -import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.state.* - -private const val DOTS = "•••" - -@Composable -internal fun TokenItemV2(config: TokenV2Config) { - when (config.tokenUIState) { - TokenUIState.LOADED -> LoadedTokenState(config) - TokenUIState.LOADING -> LoadingTokenState() - } -} - -@Composable -private fun LoadedTokenState(config: TokenV2Config) { - BaseSurface { - ConstraintLayout( - modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = TangemTheme.dimens.spacing12, - vertical = TangemTheme.dimens.spacing4, - ), - ) { - val (iconItem, tokenNameItem, fiatItem) = createRefs() - TokenIcon( - modifier = Modifier.constrainAs(iconItem) { - centerVerticallyTo(parent) - start.linkTo(parent.start) - }, - tokenIconUrl = config.iconUrl, - icon = config.icon, - networkIconRes = config.networkIcon, - ) - TokenTitleAmountBlock( - modifier = Modifier - .padding(horizontal = TangemTheme.dimens.spacing12) - .constrainAs(tokenNameItem) { - centerVerticallyTo(parent) - start.linkTo(iconItem.end) - end.linkTo(fiatItem.start) - width = Dimension.fillToConstraints - }, - title = config.name, - amount = config.amount, - hasPending = config.hasPending, - ) - TokenOptionsBlock( - config = config, - modifier = Modifier.constrainAs(fiatItem) { - centerVerticallyTo(parent) - end.linkTo(parent.end) - }, - ) - } - } -} - -/** - * Block for end part of token item - * shows status is reachable, is drag, hidden or show balance - */ -@Composable -private fun TokenOptionsBlock(config: TokenV2Config, modifier: Modifier = Modifier) { - when (config.tokenOptionsUIState) { - TokenOptionsUIState.VISIBLE -> TokenFiatPercentageBlock( - modifier = modifier, - fiatAmount = config.fiatAmount, - priceChange = config.priceChange, - ) - TokenOptionsUIState.UNREACHABLE -> Text( - modifier = modifier, - text = "Unreachable", // TODO (conform this text) - style = TangemTypography.body2, - color = TangemTheme.colors.text.tertiary, - ) - TokenOptionsUIState.HIDDEN -> TokenFiatPercentageBlock( - modifier = modifier, - fiatAmount = DOTS, - priceChange = config.priceChange, - ) - TokenOptionsUIState.DRAG -> Icon( - modifier = modifier, - painter = painterResource(id = R.drawable.ic_drag_24), - tint = TangemTheme.colors.icon.informative, - contentDescription = null, - ) - } -} - -@Composable -private fun LoadingTokenState() { - BaseSurface { - Row( - modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = TangemTheme.dimens.spacing12, - vertical = TangemTheme.dimens.spacing4, - ), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - ) { - CircleShimmer(modifier = Modifier.size(size = TangemTheme.dimens.size42)) - SpacerW12() - Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { - ShimmerRectangle( - modifier = Modifier.size( - width = TangemTheme.dimens.size72, - height = TangemTheme.dimens.size12, - ), - ) - ShimmerRectangle( - modifier = Modifier.size( - width = TangemTheme.dimens.size50, - height = TangemTheme.dimens.size12, - ), - ) - } - } - Column( - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), - ) { - ShimmerRectangle( - modifier = Modifier.size( - width = TangemTheme.dimens.size40, - height = TangemTheme.dimens.size12, - ), - ) - ShimmerRectangle( - modifier = Modifier.size( - width = TangemTheme.dimens.size40, - height = TangemTheme.dimens.size12, - ), - ) - } - } -} - -@Composable -private fun BaseSurface(onClick: (() -> Unit)? = null, content: @Composable () -> Unit) { - Surface( - modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size68), - color = TangemTheme.colors.background.primary, - onClick = onClick ?: {}, - enabled = onClick != null, - ) { - content() - } -} - -@Composable -private fun TokenTitleAmountBlock(title: String, amount: String, hasPending: Boolean, modifier: Modifier = Modifier) { - Column( - modifier = modifier, - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2), - ) { - Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { - Text( - text = title, - style = TangemTypography.subtitle2, - color = TangemTheme.colors.text.primary1, - ) - if (hasPending) { - Image( - modifier = Modifier.align(Alignment.CenterVertically), - painter = painterResource(id = R.drawable.img_loader_15), - contentDescription = null, - ) - } - } - Text( - text = amount, - style = TangemTypography.body2, - color = TangemTheme.colors.text.tertiary, - ) - } -} - -@Composable -private fun TokenFiatPercentageBlock(fiatAmount: String, priceChange: PriceChange, modifier: Modifier = Modifier) { - Column(modifier = modifier.requiredWidth(IntrinsicSize.Max)) { - Text( - modifier = Modifier.align(Alignment.End), - text = fiatAmount, - style = TangemTypography.body2, - color = TangemTheme.colors.text.primary1, - ) - SpacerH2() - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.End, - ) { - val iconChangeArrow: Int - val changeTextColor: Color - when (priceChange.type) { - PriceChangeType.UP -> { - iconChangeArrow = R.drawable.img_arrow_up_8 - changeTextColor = TangemTheme.colors.text.accent - } - PriceChangeType.DOWN -> { - iconChangeArrow = R.drawable.img_arrow_down_8 - changeTextColor = TangemTheme.colors.text.warning - } - } - Image( - modifier = Modifier.align(Alignment.CenterVertically), - painter = painterResource(id = iconChangeArrow), - contentDescription = null, - ) - SpacerW4() - Text( - modifier = Modifier.align(Alignment.CenterVertically), - text = priceChange.valuePercent, - style = TangemTypography.body2, - color = changeTextColor, - ) - } - } -} - -@Composable -private fun TokenIcon( - tokenIconUrl: String?, - modifier: Modifier = Modifier, - @DrawableRes icon: Int? = null, - @DrawableRes networkIconRes: Int? = null, -) { - Box( - modifier = modifier - .padding(end = TangemTheme.dimens.spacing16) - .size(TangemTheme.dimens.size42), - ) { - val tokenImageModifier = Modifier - .align(Alignment.BottomStart) - .size(TangemTheme.dimens.size36) - - val data = if (tokenIconUrl.isNullOrEmpty()) { - icon - } else { - tokenIconUrl - } - SubcomposeAsyncImage( - modifier = tokenImageModifier, - model = ImageRequest.Builder(LocalContext.current) - .data(data) - .crossfade(true) - .build(), - loading = { CircleShimmer(modifier = tokenImageModifier) }, - contentDescription = null, - ) - - if (networkIconRes != null) { - Box( - modifier = Modifier - .align(Alignment.TopEnd) - .size(TangemTheme.dimens.size18) - .background(color = Color.White, shape = CircleShape), - contentAlignment = Alignment.Center, - ) { - Image( - modifier = Modifier.padding(all = 0.5.dp), - painter = painterResource(id = networkIconRes), - contentDescription = null, - ) - } - } - } -} - -// region preview - -@Composable -private fun TokensPreview() { - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18), - ) { - val config = TokenV2Config( - name = "Polygon", - amount = "5,412 MATIC", - fiatAmount = "321 $", - priceChange = PriceChange( - valuePercent = "2%", - type = PriceChangeType.UP, - ), - iconUrl = null, - icon = R.drawable.img_polygon_22, - networkIcon = R.drawable.img_polygon_22, - tokenUIState = TokenUIState.LOADED, - tokenOptionsUIState = TokenOptionsUIState.VISIBLE, - hasPending = true, - ) - // Loaded item - TokenItemV2(config) - // Unreachable item - TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.UNREACHABLE)) - // Drag item - TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.DRAG)) - // Hidden item - TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.UNREACHABLE)) - // Loading item - TokenItemV2( - config.copy( - tokenUIState = TokenUIState.LOADING, - ), - ) - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview_Tokens_InLightTheme() { - TangemTheme(isDark = false) { - TokensPreview() - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview_Tokens_InDarkTheme() { - TangemTheme(isDark = true) { - TokensPreview() - } -} - -// endregion preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt deleted file mode 100644 index b716022f9b..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.tangem.feature.wallet.presentation.ui - -import com.tangem.core.ui.R -import com.tangem.feature.wallet.presentation.state.WalletCardState -import com.tangem.feature.wallet.presentation.state.WalletStateHolder -import java.util.UUID - -internal object WalletPreviewData { - - val walletCardContent = WalletCardState.Content( - id = UUID.randomUUID().toString(), - title = "Wallet 1", - balance = "8923,05 $", - additionalInfo = "3 cards • Seed enabled", - imageResId = R.drawable.ill_businessman_3d, - onClick = {}, - ) - - val walletCardLoading = WalletCardState.Loading( - id = UUID.randomUUID().toString(), - title = "Wallet 1", - additionalInfo = "3 cards • Seed enabled", - imageResId = R.drawable.ill_businessman_3d, - onClick = {}, - ) - - val walletCardHiddenContent = WalletCardState.HiddenContent( - id = UUID.randomUUID().toString(), - title = "Wallet 1", - additionalInfo = "3 cards • Seed enabled", - imageResId = R.drawable.ill_businessman_3d, - onClick = {}, - ) - - val walletCardError = WalletCardState.Error( - id = UUID.randomUUID().toString(), - title = "Wallet 1", - additionalInfo = "3 cards • Seed enabled", - imageResId = R.drawable.ill_businessman_3d, - onClick = {}, - ) - - val walletScreenState = WalletStateHolder( - onBackClick = {}, - headerConfig = WalletStateHolder.HeaderConfig( - wallets = listOf(walletCardContent, walletCardLoading, walletCardHiddenContent, walletCardError), - onScanCardClick = {}, - onMoreClick = {}, - ), - ) -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt similarity index 97% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletCardState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt index 0837a3ef7d..7685a267da 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.state +package com.tangem.feature.wallet.presentation.wallet.state import androidx.annotation.DrawableRes diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt similarity index 79% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletStateHolder.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt index c69ab425ae..8142667ad3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt @@ -1,4 +1,6 @@ -package com.tangem.feature.wallet.presentation.state +package com.tangem.feature.wallet.presentation.wallet.state + +import kotlinx.collections.immutable.ImmutableList /** * Wallet state holder @@ -21,7 +23,7 @@ internal data class WalletStateHolder( * @property onMoreClick lambda be invoked when more button is clicked */ data class HeaderConfig( - val wallets: List, + val wallets: ImmutableList, val onScanCardClick: () -> Unit, val onMoreClick: () -> Unit, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt similarity index 69% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletScreen.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index dc166a77a2..ba17e6e692 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -1,10 +1,10 @@ -package com.tangem.feature.wallet.presentation.ui +package com.tangem.feature.wallet.presentation.wallet.ui import androidx.activity.compose.BackHandler import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable -import com.tangem.feature.wallet.presentation.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.ui.components.WalletHeader +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletHeader /** * Wallet screen diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt similarity index 95% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletCard.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt index 25a427408a..562c954023 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.ui.components +package com.tangem.feature.wallet.presentation.wallet.ui.components import androidx.annotation.DrawableRes import androidx.compose.foundation.Image @@ -17,12 +17,12 @@ import androidx.compose.ui.unit.sp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import com.tangem.core.ui.components.FontSizeRange +import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.ResizableText -import com.tangem.core.ui.components.ShimmerRectangle import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.state.WalletCardState -import com.tangem.feature.wallet.presentation.ui.WalletPreviewData +import com.tangem.feature.wallet.presentation.common.WalletPreviewData +import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState private const val DOTS = "•••" @@ -119,7 +119,7 @@ private fun Balance(state: WalletCardState) { ) } is WalletCardState.Loading -> { - ShimmerRectangle( + RectangleShimmer( modifier = Modifier.size( width = TangemTheme.dimens.size102, height = TangemTheme.dimens.size24, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletHeader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletHeader.kt similarity index 91% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletHeader.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletHeader.kt index acfa7b9a49..cb01b48377 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletHeader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletHeader.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.ui.components +package com.tangem.feature.wallet.presentation.wallet.ui.components import androidx.compose.animation.core.* import androidx.compose.foundation.ExperimentalFoundationApi @@ -21,9 +21,10 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter 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.state.WalletCardState -import com.tangem.feature.wallet.presentation.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.ui.WalletPreviewData +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 @@ -109,7 +110,7 @@ private fun Preview_WalletHeader_DarkTheme( private class WalletHeaderProvider : CollectionPreviewParameterProvider( collection = listOf( WalletStateHolder.HeaderConfig( - wallets = listOf( + wallets = persistentListOf( WalletPreviewData.walletCardContent, WalletPreviewData.walletCardLoading, WalletPreviewData.walletCardHiddenContent, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt similarity index 83% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/viewmodels/WalletViewModel.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index c69e4f439b..a565bb8c91 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -1,12 +1,12 @@ -package com.tangem.feature.wallet.presentation.viewmodels +package com.tangem.feature.wallet.presentation.wallet.viewmodels import androidx.compose.runtime.getValue 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.router.InnerWalletRouter -import com.tangem.feature.wallet.presentation.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.ui.WalletPreviewData +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlin.properties.Delegates From 57e00dae8c3aebf8d246186b0030c8d7233e7b67 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Jun 2023 17:16:59 +0300 Subject: [PATCH 4/4] Updated on 2026-08-14 --- .../AddCustomTokenFloatingButton.kt | 7 +- .../legacy/compose/AddCustomTokenScreen.kt | 41 +--- .../ui/common/DetailsComposeElements.kt | 24 +- .../WallectSelectorScreenContent.kt | 15 +- .../ui/components/WelcomeScreenContent.kt | 17 +- .../com/tangem/core/ui/components/Buttons.kt | 205 ++++++++++++------ .../core/ui/components/SuccessScreen.kt | 14 +- .../wallet2/ui/CheckSeedPhraseScreen.kt | 7 +- .../wallet2/ui/ImportSeedPhraseScreen.kt | 7 +- .../presentation/wallet2/ui/IntroScreen.kt | 6 +- .../ui/SwapPermissionBottomSheetContent.kt | 4 +- .../feature/swap/ui/SwapScreenContent.kt | 4 +- 12 files changed, 178 insertions(+), 173 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenFloatingButton.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenFloatingButton.kt index 618a46fd70..5011ca010c 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenFloatingButton.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenFloatingButton.kt @@ -5,12 +5,11 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource 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.PrimaryButtonIconLeft +import com.tangem.core.ui.components.PrimaryButtonIconStart import com.tangem.core.ui.res.TangemTheme import com.tangem.tap.features.customtoken.impl.presentation.models.AddCustomTokenFloatingButton import com.tangem.wallet.R @@ -25,13 +24,13 @@ import com.tangem.wallet.R */ @Composable internal fun AddCustomTokenFloatingButton(model: AddCustomTokenFloatingButton, modifier: Modifier = Modifier) { - PrimaryButtonIconLeft( + PrimaryButtonIconStart( modifier = modifier .imePadding() .padding(horizontal = TangemTheme.dimens.spacing16) .fillMaxWidth(), text = stringResource(id = R.string.common_add), - icon = painterResource(id = R.drawable.ic_plus_24), + iconResId = R.drawable.ic_plus_24, enabled = model.isEnabled, onClick = model.onClick, ) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/AddCustomTokenScreen.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/AddCustomTokenScreen.kt index 0a4ad3b4a6..baa487cec7 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/AddCustomTokenScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/AddCustomTokenScreen.kt @@ -1,48 +1,21 @@ package com.tangem.tap.features.customtoken.legacy.compose -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.BottomSheetScaffold -import androidx.compose.material.BottomSheetScaffoldState -import androidx.compose.material.BottomSheetState -import androidx.compose.material.BottomSheetValue -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.FabPosition -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold -import androidx.compose.material.Surface -import androidx.compose.material.rememberBottomSheetScaffoldState -import androidx.compose.material.rememberScaffoldState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.material.* +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.tangem.core.ui.components.PrimaryButtonIconLeft +import com.tangem.core.ui.components.PrimaryButtonIconStart import com.tangem.core.ui.components.keyboardAsState import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.AddCustomTokenError import com.tangem.domain.common.form.DataField import com.tangem.domain.common.form.FieldId -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.ContractAddress -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Decimals -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.DerivationPath -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Name -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Network -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Symbol +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState import com.tangem.domain.features.addCustomToken.redux.ScreenState @@ -189,12 +162,12 @@ fun Warnings(warnings: List) { @Composable private fun AddButton(state: MutableState) { - PrimaryButtonIconLeft( + PrimaryButtonIconStart( modifier = Modifier .padding(horizontal = TangemTheme.dimens.spacing16) .fillMaxWidth(), text = stringResource(id = R.string.common_add), - icon = painterResource(id = R.drawable.ic_plus_24), + iconResId = R.drawable.ic_plus_24, enabled = state.value.screenState.addButton.isEnabled, onClick = { domainStore.dispatch(AddCustomTokenAction.OnAddCustomTokenClicked) }, ) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt b/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt index 38fdbe45f6..c92047d0bc 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt @@ -1,23 +1,9 @@ package com.tangem.tap.features.details.ui.common import androidx.activity.compose.BackHandler -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.selection.selectable -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.RadioButton -import androidx.compose.material.RadioButtonDefaults -import androidx.compose.material.Scaffold -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar +import androidx.compose.material.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -25,7 +11,7 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.tangem.core.ui.components.PrimaryButtonIconRight +import com.tangem.core.ui.components.PrimaryButtonIconEnd import com.tangem.core.ui.res.TangemTheme import com.tangem.wallet.R @@ -110,13 +96,13 @@ fun EmptyTopBarWithNavigation( @Composable fun DetailsMainButton(title: String, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true) { - PrimaryButtonIconRight( + PrimaryButtonIconEnd( text = title, enabled = enabled, onClick = onClick, modifier = modifier .fillMaxWidth(), - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, ) } diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt index 71d7f6a253..f0dd6b139b 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt @@ -3,14 +3,7 @@ package com.tangem.tap.features.walletSelector.ui.components import androidx.annotation.StringRes import androidx.compose.foundation.ExperimentalFoundationApi 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.size +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.Icon @@ -28,7 +21,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.components.PrimaryButton -import com.tangem.core.ui.components.SecondaryButtonIconRight +import com.tangem.core.ui.components.SecondaryButtonIconEnd import com.tangem.core.ui.components.SpacerWMax import com.tangem.core.ui.components.atoms.Hand import com.tangem.core.ui.res.TangemTheme @@ -191,11 +184,11 @@ private fun Footer( onClick = onUnlockClick, ) } - SecondaryButtonIconRight( + SecondaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.user_wallet_list_add_button), showProgress = showAddCardProgress, - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, onClick = onAddCardClick, ) } diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WelcomeScreenContent.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WelcomeScreenContent.kt index 373d370eda..cc83e0d893 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WelcomeScreenContent.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WelcomeScreenContent.kt @@ -1,11 +1,7 @@ package com.tangem.tap.features.welcome.ui.components import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -15,12 +11,7 @@ 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 com.tangem.core.ui.components.PrimaryButtonIconRight -import com.tangem.core.ui.components.SecondaryButton -import com.tangem.core.ui.components.SpacerH12 -import com.tangem.core.ui.components.SpacerH16 -import com.tangem.core.ui.components.SpacerH32 -import com.tangem.core.ui.components.SpacerHMax +import com.tangem.core.ui.components.* import com.tangem.core.ui.res.TangemTheme import com.tangem.wallet.R @@ -76,13 +67,13 @@ internal fun WelcomeScreenContent( onClick = onUnlockClick, ) SpacerH12() - PrimaryButtonIconRight( + PrimaryButtonIconEnd( modifier = Modifier .padding(horizontal = TangemTheme.dimens.spacing16) .fillMaxWidth(), text = stringResource(R.string.welcome_unlock_card), showProgress = showScanCardProgress, - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, onClick = onScanCardClick, ) SpacerH16() 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 8e63ac74d1..00a6b2ac03 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 @@ -1,5 +1,6 @@ package com.tangem.core.ui.components +import androidx.annotation.DrawableRes import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material.* @@ -8,7 +9,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview @@ -39,9 +39,9 @@ fun TextButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier, * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=97%3A62&t=TmfD6UBHPg9uYfev-4) * */ @Composable -fun TextButtonIconLeft( +fun TextButtonIconStart( text: String, - icon: Painter, + @DrawableRes iconResId: Int, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, @@ -49,7 +49,7 @@ fun TextButtonIconLeft( TangemButton( modifier = modifier, text = text, - icon = TangemButtonIcon.Left(icon), + icon = TangemButtonIcon.Start(iconResId), onClick = onClick, enabled = enabled, showProgress = false, @@ -97,9 +97,9 @@ fun PrimaryButton( * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=68%3A47&t=TmfD6UBHPg9uYfev-4) * */ @Composable -fun PrimaryButtonIconRight( +fun PrimaryButtonIconEnd( text: String, - icon: Painter, + @DrawableRes iconResId: Int, onClick: () -> Unit, modifier: Modifier = Modifier, showProgress: Boolean = false, @@ -108,7 +108,7 @@ fun PrimaryButtonIconRight( TangemButton( modifier = modifier, text = text, - icon = TangemButtonIcon.Right(icon), + icon = TangemButtonIcon.End(iconResId), onClick = onClick, colors = TangemButtonsDefaults.primaryButtonColors, enabled = enabled, @@ -120,9 +120,9 @@ fun PrimaryButtonIconRight( * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=233%3A258&t=TmfD6UBHPg9uYfev-4) * */ @Composable -fun PrimaryButtonIconLeft( +fun PrimaryButtonIconStart( text: String, - icon: Painter, + @DrawableRes iconResId: Int, onClick: () -> Unit, modifier: Modifier = Modifier, showProgress: Boolean = false, @@ -131,7 +131,7 @@ fun PrimaryButtonIconLeft( TangemButton( modifier = modifier, text = text, - icon = TangemButtonIcon.Left(icon), + icon = TangemButtonIcon.Start(iconResId), onClick = onClick, colors = TangemButtonsDefaults.primaryButtonColors, enabled = enabled, @@ -164,9 +164,9 @@ fun SecondaryButton( * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=99%3A50&t=TmfD6UBHPg9uYfev-4) * */ @Composable -fun SecondaryButtonIconRight( +fun SecondaryButtonIconEnd( text: String, - icon: Painter, + @DrawableRes iconResId: Int, onClick: () -> Unit, modifier: Modifier = Modifier, showProgress: Boolean = false, @@ -175,7 +175,7 @@ fun SecondaryButtonIconRight( TangemButton( modifier = modifier, text = text, - icon = TangemButtonIcon.Right(icon), + icon = TangemButtonIcon.End(iconResId), onClick = onClick, colors = TangemButtonsDefaults.secondaryButtonColors, enabled = enabled, @@ -187,9 +187,9 @@ fun SecondaryButtonIconRight( * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=233%3A262&t=TmfD6UBHPg9uYfev-4) * */ @Composable -fun SecondaryButtonIconLeft( +fun SecondaryButtonIconStart( text: String, - icon: Painter, + @DrawableRes iconResId: Int, onClick: () -> Unit, modifier: Modifier = Modifier, showProgress: Boolean = false, @@ -198,7 +198,7 @@ fun SecondaryButtonIconLeft( TangemButton( modifier = modifier, text = text, - icon = TangemButtonIcon.Left(icon), + icon = TangemButtonIcon.Start(iconResId), onClick = onClick, colors = TangemButtonsDefaults.secondaryButtonColors, enabled = enabled, @@ -214,7 +214,7 @@ fun SelectorButton(text: String, onClick: () -> Unit, modifier: Modifier = Modif modifier = modifier, text = text, textStyle = TangemTheme.typography.subtitle2, - icon = TangemButtonIcon.Right(painterResource(id = R.drawable.ic_chevron_24)), + icon = TangemButtonIcon.End(iconResId = R.drawable.ic_chevron_24), onClick = onClick, colors = TangemButtonsDefaults.selectorButtonColors, showProgress = false, @@ -232,7 +232,7 @@ fun SelectorButton(text: String, onClick: () -> Unit, modifier: Modifier = Modif @Composable fun RoundedActionButton( text: String, - icon: Painter, + @DrawableRes iconResId: Int, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, @@ -240,7 +240,7 @@ fun RoundedActionButton( TangemButton( modifier = modifier, text = text, - icon = TangemButtonIcon.Left(icon), + icon = TangemButtonIcon.Start(iconResId), onClick = onClick, enabled = enabled, showProgress = false, @@ -255,7 +255,7 @@ fun RoundedActionButton( @Composable fun ActionButton( text: String, - icon: Painter, + @DrawableRes iconResId: Int, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, @@ -263,7 +263,7 @@ fun ActionButton( TangemButton( modifier = modifier, text = text, - icon = TangemButtonIcon.Left(icon), + icon = TangemButtonIcon.Start(iconResId), onClick = onClick, enabled = enabled, showProgress = false, @@ -278,7 +278,7 @@ fun ActionButton( @Composable fun BackgroundActionButton( text: String, - icon: Painter, + @DrawableRes iconResId: Int, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, @@ -286,7 +286,7 @@ fun BackgroundActionButton( TangemButton( modifier = modifier, text = text, - icon = TangemButtonIcon.Left(icon), + icon = TangemButtonIcon.Start(iconResId), onClick = onClick, enabled = enabled, showProgress = false, @@ -324,6 +324,7 @@ private fun TangemButton( elevation = elevation, shape = size.toShape(), colors = colors, + contentPadding = size.toContentPadding(icon = icon), ) { ButtonContent( text = text, @@ -348,10 +349,10 @@ private fun ButtonContent( enabled: Boolean, showProgress: Boolean, ) { - val icon = @Composable { painter: Painter -> + val icon = @Composable { iconResId: Int -> Icon( modifier = Modifier.size(TangemTheme.dimens.size20), - painter = painter, + painter = painterResource(id = iconResId), tint = colors.contentColor(enabled = enabled).value, contentDescription = null, ) @@ -372,16 +373,17 @@ private fun ButtonContent( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(size.toIconPadding()), ) { - if (buttonIcon is TangemButtonIcon.Left) { - icon(buttonIcon.painter) + if (buttonIcon is TangemButtonIcon.Start) { + icon(buttonIcon.iconResId) } Text( text = text, style = textStyle, color = colors.contentColor(enabled = enabled).value, + maxLines = 1, ) - if (buttonIcon is TangemButtonIcon.Right) { - icon(buttonIcon.painter) + if (buttonIcon is TangemButtonIcon.End) { + icon(buttonIcon.iconResId) } } } @@ -389,14 +391,14 @@ private fun ButtonContent( @Immutable private sealed interface TangemButtonIcon { - val painter: Painter? + val iconResId: Int? - data class Left(override val painter: Painter) : TangemButtonIcon + data class Start(override val iconResId: Int) : TangemButtonIcon - data class Right(override val painter: Painter) : TangemButtonIcon + data class End(override val iconResId: Int) : TangemButtonIcon object None : TangemButtonIcon { - override val painter: Painter? = null + override val iconResId: Int? = null } } @@ -409,6 +411,7 @@ private enum class TangemButtonSize { } @Composable +@ReadOnlyComposable private fun TangemButtonSize.toHeightDp(): Dp = when (this) { TangemButtonSize.Default -> TangemTheme.dimens.size48 TangemButtonSize.Text -> TangemTheme.dimens.size40 @@ -419,6 +422,7 @@ private fun TangemButtonSize.toHeightDp(): Dp = when (this) { } @Composable +@ReadOnlyComposable private fun TangemButtonSize.toShape(): Shape = when (this) { TangemButtonSize.Default -> TangemTheme.shapes.roundedCornersMedium TangemButtonSize.Text -> TangemTheme.shapes.roundedCornersSmall @@ -428,6 +432,7 @@ private fun TangemButtonSize.toShape(): Shape = when (this) { } @Composable +@ReadOnlyComposable private fun TangemButtonSize.toIconPadding(): Dp = when (this) { TangemButtonSize.Default -> TangemTheme.dimens.spacing8 TangemButtonSize.Text -> TangemTheme.dimens.spacing8 @@ -437,7 +442,63 @@ private fun TangemButtonSize.toIconPadding(): Dp = when (this) { -> TangemTheme.dimens.spacing8 } -object TangemButtonsDefaults { +@Composable +@ReadOnlyComposable +private fun TangemButtonSize.toContentPadding(icon: TangemButtonIcon): PaddingValues { + val horizontalPadding = this.toHorizontalContentPadding(icon = icon) + + return when (this) { + TangemButtonSize.Default -> PaddingValues( + top = TangemTheme.dimens.spacing14, + bottom = TangemTheme.dimens.spacing14, + start = horizontalPadding.first, + end = horizontalPadding.second, + ) + TangemButtonSize.Text -> PaddingValues( + top = TangemTheme.dimens.spacing10, + bottom = TangemTheme.dimens.spacing10, + start = horizontalPadding.first, + end = horizontalPadding.second, + ) + TangemButtonSize.Selector -> PaddingValues( + top = TangemTheme.dimens.spacing0_5, + bottom = TangemTheme.dimens.spacing0_5, + start = horizontalPadding.first, + end = horizontalPadding.second, + ) + TangemButtonSize.Action, + TangemButtonSize.RoundedAction, + -> PaddingValues( + top = TangemTheme.dimens.spacing8, + bottom = TangemTheme.dimens.spacing8, + start = horizontalPadding.first, + end = horizontalPadding.second, + ) + } +} + +@Composable +@ReadOnlyComposable +private fun TangemButtonSize.toHorizontalContentPadding(icon: TangemButtonIcon): Pair { + return when (this) { + TangemButtonSize.Default -> TangemTheme.dimens.spacing32 to TangemTheme.dimens.spacing32 + TangemButtonSize.Text -> when (icon) { + is TangemButtonIcon.None -> TangemTheme.dimens.spacing16 to TangemTheme.dimens.spacing16 + is TangemButtonIcon.Start -> TangemTheme.dimens.spacing14 to TangemTheme.dimens.spacing16 + is TangemButtonIcon.End -> TangemTheme.dimens.spacing16 to TangemTheme.dimens.spacing14 + } + TangemButtonSize.Selector -> TangemTheme.dimens.spacing0_5 to TangemTheme.dimens.spacing0_5 + TangemButtonSize.Action, + TangemButtonSize.RoundedAction, + -> when (icon) { + is TangemButtonIcon.None -> TangemTheme.dimens.spacing24 to TangemTheme.dimens.spacing24 + is TangemButtonIcon.Start -> TangemTheme.dimens.spacing16 to TangemTheme.dimens.spacing24 + is TangemButtonIcon.End -> TangemTheme.dimens.spacing24 to TangemTheme.dimens.spacing16 + } + } +} + +private object TangemButtonsDefaults { val elevation: ButtonElevation @Composable get() = ButtonDefaults .elevation( @@ -446,7 +507,9 @@ object TangemButtonsDefaults { ) val primaryButtonColors: ButtonColors - @Composable get() = TangemButtonColors( + @Composable + @ReadOnlyComposable + get() = TangemButtonColors( backgroundColor = TangemTheme.colors.button.primary, contentColor = TangemTheme.colors.text.primary2, disabledBackgroundColor = TangemTheme.colors.button.disabled, @@ -454,7 +517,9 @@ object TangemButtonsDefaults { ) val secondaryButtonColors: ButtonColors - @Composable get() = TangemButtonColors( + @Composable + @ReadOnlyComposable + get() = TangemButtonColors( backgroundColor = TangemTheme.colors.button.secondary, contentColor = TangemTheme.colors.text.primary1, disabledBackgroundColor = TangemTheme.colors.button.disabled, @@ -462,7 +527,9 @@ object TangemButtonsDefaults { ) val defaultTextButtonColors: ButtonColors - @Composable get() = TangemButtonColors( + @Composable + @ReadOnlyComposable + get() = TangemButtonColors( backgroundColor = Color.Transparent, contentColor = TangemTheme.colors.text.secondary, disabledBackgroundColor = Color.Transparent, @@ -470,7 +537,9 @@ object TangemButtonsDefaults { ) val warningTextButtonColors: ButtonColors - @Composable get() = TangemButtonColors( + @Composable + @ReadOnlyComposable + get() = TangemButtonColors( backgroundColor = Color.Transparent, contentColor = TangemTheme.colors.text.warning, disabledBackgroundColor = Color.Transparent, @@ -478,7 +547,9 @@ object TangemButtonsDefaults { ) val selectorButtonColors: ButtonColors - @Composable get() = TangemButtonColors( + @Composable + @ReadOnlyComposable + get() = TangemButtonColors( backgroundColor = Color.Transparent, contentColor = TangemTheme.colors.text.tertiary, disabledBackgroundColor = Color.Transparent, @@ -486,7 +557,9 @@ object TangemButtonsDefaults { ) val backgroundButtonColors: ButtonColors - @Composable get() = TangemButtonColors( + @Composable + @ReadOnlyComposable + get() = TangemButtonColors( backgroundColor = TangemTheme.colors.background.primary, contentColor = TangemTheme.colors.text.primary1, disabledBackgroundColor = TangemTheme.colors.button.disabled, @@ -533,17 +606,17 @@ private fun PrimaryButtonSample(modifier: Modifier = Modifier) { onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - PrimaryButtonIconRight( + PrimaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - PrimaryButtonIconLeft( + PrimaryButtonIconStart( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) @@ -554,18 +627,18 @@ private fun PrimaryButtonSample(modifier: Modifier = Modifier) { onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - PrimaryButtonIconRight( + PrimaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, enabled = false, onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - PrimaryButtonIconLeft( + PrimaryButtonIconStart( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, enabled = false, onClick = { /* no-op */ }, ) @@ -607,17 +680,17 @@ private fun SecondaryButtonSample(modifier: Modifier = Modifier) { onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - SecondaryButtonIconRight( + SecondaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - SecondaryButtonIconLeft( + SecondaryButtonIconStart( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) @@ -628,18 +701,18 @@ private fun SecondaryButtonSample(modifier: Modifier = Modifier) { onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - SecondaryButtonIconRight( + SecondaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, enabled = false, onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - SecondaryButtonIconLeft( + SecondaryButtonIconStart( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, enabled = false, onClick = { /* no-op */ }, ) @@ -673,9 +746,9 @@ private fun TextButtonSample(modifier: Modifier = Modifier) { onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - TextButtonIconLeft( + TextButtonIconStart( text = "Enabled", - icon = painterResource(id = R.drawable.ic_plus_24), + iconResId = R.drawable.ic_plus_24, onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) @@ -685,9 +758,9 @@ private fun TextButtonSample(modifier: Modifier = Modifier) { onClick = { /* no-op */ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - TextButtonIconLeft( + TextButtonIconStart( text = "Enabled", - icon = painterResource(id = R.drawable.ic_plus_24), + iconResId = R.drawable.ic_plus_24, enabled = false, onClick = { /* no-op */ }, ) @@ -728,39 +801,39 @@ private fun ActionButtonSample(modifier: Modifier = Modifier) { ) { RoundedActionButton( text = "Send", - icon = painterResource(id = R.drawable.ic_arrow_up_24), + iconResId = R.drawable.ic_arrow_up_24, onClick = { /* [REDACTED_TODO_COMMENT]*/ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) ActionButton( text = "Send", - icon = painterResource(id = R.drawable.ic_arrow_up_24), + iconResId = R.drawable.ic_arrow_up_24, onClick = { /* [REDACTED_TODO_COMMENT]*/ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) BackgroundActionButton( text = "Send", - icon = painterResource(id = R.drawable.ic_arrow_up_24), + iconResId = R.drawable.ic_arrow_up_24, onClick = { /* [REDACTED_TODO_COMMENT]*/ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) RoundedActionButton( text = "Send", - icon = painterResource(id = R.drawable.ic_arrow_up_24), + iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { /* [REDACTED_TODO_COMMENT]*/ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) ActionButton( text = "Send", - icon = painterResource(id = R.drawable.ic_arrow_up_24), + iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { /* [REDACTED_TODO_COMMENT]*/ }, ) Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) BackgroundActionButton( text = "Send", - icon = painterResource(id = R.drawable.ic_arrow_up_24), + iconResId = R.drawable.ic_arrow_up_24, enabled = false, onClick = { /* [REDACTED_TODO_COMMENT]*/ }, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/SuccessScreen.kt b/core/ui/src/main/java/com/tangem/core/ui/components/SuccessScreen.kt index ebab2d5145..c5c3feabc1 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/SuccessScreen.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/SuccessScreen.kt @@ -3,15 +3,7 @@ package com.tangem.core.ui.components import androidx.annotation.DrawableRes import androidx.annotation.StringRes 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.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll @@ -143,9 +135,9 @@ private fun SecondaryButtonForResultScreen( @DrawableRes secondaryButtonIcon: Int? = null, ) { if (secondaryButtonIcon != null) { - SecondaryButtonIconLeft( + SecondaryButtonIconStart( text = stringResource(id = secondaryButtonText), - icon = painterResource(id = secondaryButtonIcon), + iconResId = secondaryButtonIcon, onClick = onSecondaryButtonClick, modifier = Modifier.fillMaxWidth(), ) diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/CheckSeedPhraseScreen.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/CheckSeedPhraseScreen.kt index b67af3906f..2edb3e416c 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/CheckSeedPhraseScreen.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/CheckSeedPhraseScreen.kt @@ -4,11 +4,10 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.tangem.core.ui.R import com.tangem.core.ui.components.OutlineTextField -import com.tangem.core.ui.components.PrimaryButtonIconLeft +import com.tangem.core.ui.components.PrimaryButtonIconStart import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.onboarding.presentation.wallet2.model.CheckSeedPhraseState import com.tangem.feature.onboarding.presentation.wallet2.ui.components.Description @@ -57,11 +56,11 @@ fun CheckSeedPhraseScreen(state: CheckSeedPhraseState, modifier: Modifier = Modi } OnboardingActionBlock( firstActionContent = { - PrimaryButtonIconLeft( + PrimaryButtonIconStart( modifier = Modifier .fillMaxWidth(), text = stringResource(id = R.string.onboarding_create_wallet_button_create_wallet), - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, enabled = state.buttonCreateWallet.enabled, showProgress = state.buttonCreateWallet.showProgress, onClick = state.buttonCreateWallet.onClick, diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt index 805aee6175..d29fd51838 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt @@ -9,12 +9,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.unit.IntOffset import com.tangem.core.ui.components.PrimaryButton -import com.tangem.core.ui.components.PrimaryButtonIconLeft +import com.tangem.core.ui.components.PrimaryButtonIconStart import com.tangem.core.ui.components.TangemTextFieldsDefault import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.onboarding.R @@ -56,11 +55,11 @@ fun ImportSeedPhraseScreen(state: ImportSeedPhraseState, modifier: Modifier = Mo Box { OnboardingActionBlock( firstActionContent = { - PrimaryButtonIconLeft( + PrimaryButtonIconStart( modifier = Modifier .fillMaxWidth(), text = stringResource(id = R.string.onboarding_create_wallet_button_create_wallet), - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, enabled = state.buttonCreateWallet.enabled, showProgress = state.buttonCreateWallet.showProgress, onClick = state.buttonCreateWallet.onClick, diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/IntroScreen.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/IntroScreen.kt index c99a233f33..cba03f9df9 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/IntroScreen.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/IntroScreen.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import coil.compose.SubcomposeAsyncImage import coil.request.ImageRequest -import com.tangem.core.ui.components.PrimaryButtonIconLeft +import com.tangem.core.ui.components.PrimaryButtonIconStart import com.tangem.core.ui.components.SecondaryButton import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.onboarding.R @@ -46,11 +46,11 @@ fun IntroScreen(state: IntroState, modifier: Modifier = Modifier) { Box { OnboardingActionBlock( firstActionContent = { - PrimaryButtonIconLeft( + PrimaryButtonIconStart( modifier = Modifier .fillMaxWidth(), text = stringResource(id = R.string.onboarding_create_wallet_button_create_wallet), - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, enabled = state.buttonCreateWallet.enabled, showProgress = state.buttonCreateWallet.showProgress, onClick = state.buttonCreateWallet.onClick, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheetContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheetContent.kt index 1f87fc061f..6f8bee6fe7 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheetContent.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheetContent.kt @@ -74,9 +74,9 @@ fun SwapPermissionBottomSheetContent(data: SwapPermissionState.ReadyForRequest, SpacerH28() - PrimaryButtonIconRight( + PrimaryButtonIconEnd( text = stringResource(id = R.string.swapping_permission_buttons_approve), - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, modifier = Modifier.fillMaxWidth(), onClick = data.approveButton.onClick, ) 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 b7a127af0b..27e4b3c750 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 @@ -345,10 +345,10 @@ private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> U ) } else -> { - PrimaryButtonIconRight( + PrimaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = stringResource(id = R.string.swapping_swap), - icon = painterResource(id = R.drawable.ic_tangem_24), + iconResId = R.drawable.ic_tangem_24, enabled = state.swapButton.enabled, showProgress = state.swapButton.loading, onClick = state.swapButton.onClick,