Updated on 2026-08-14
This commit is contained in:
commit
f6c438419f
49 changed files with 1344 additions and 774 deletions
|
|
@ -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<Any>) : 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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,10 @@ internal fun List<WalletDataModel>.updateWithMissedDerivation(): List<WalletData
|
|||
internal fun List<WalletDataModel>.updateWithUnreachable(): List<WalletDataModel> {
|
||||
return this.map { walletData ->
|
||||
walletData.copy(
|
||||
status = WalletDataModel.Unreachable(errorMessage = null),
|
||||
status = WalletDataModel.Unreachable(
|
||||
errorMessage = null,
|
||||
amount = walletData.status.amount,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<AddCustomTokenError.Warning>) {
|
|||
|
||||
@Composable
|
||||
private fun AddButton(state: MutableState<AddCustomTokenState>) {
|
||||
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) },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ sealed class WalletAction : Action {
|
|||
val swapAllowed: Boolean,
|
||||
) : DialogAction()
|
||||
|
||||
data class ChooseCurrency(val amounts: List<Amount>?) : DialogAction()
|
||||
data class ChooseCurrency(val amounts: List<Amount>) : DialogAction()
|
||||
data class RussianCardholdersWarningDialog(
|
||||
val dialogData: WalletDialog.RussianCardholdersWarningDialog.Data? = null,
|
||||
) : DialogAction()
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ class WalletDialogsMiddleware {
|
|||
)
|
||||
}
|
||||
is WalletAction.DialogAction.ChooseCurrency -> {
|
||||
if (action.amounts.isEmpty()) return
|
||||
|
||||
store.dispatchDialogShow(
|
||||
WalletDialog.SelectAmountToSendDialog(
|
||||
amounts = action.amounts,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Amount>?) : WalletDialog
|
||||
data class SelectAmountToSendDialog(val amounts: List<Amount>) : WalletDialog
|
||||
object SignedHashesMultiWalletDialog : WalletDialog
|
||||
data class ChooseTradeActionDialog(
|
||||
val buyAllowed: Boolean,
|
||||
|
|
|
|||
|
|
@ -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<Amount, ChooseAmountAdapter.AmountViewHolder>(DiffUtilCallback) {
|
||||
private class ChooseAmountAdapter : ListAdapter<Amount, ChooseAmountAdapter.AmountViewHolder>(DiffUtilCallback) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AmountViewHolder {
|
||||
val binding = ItemWalletAmountToSendBinding.inflate(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
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)
|
||||
}
|
||||
|
|
@ -473,6 +473,7 @@
|
|||
<string name="wallet_error_no_account">Аккаунт не создан</string>
|
||||
<string name="wallet_error_unsupported_blockchain">Эта карта не поддерживается</string>
|
||||
<string name="wallet_error_unsupported_blockchain_subtitle">Ваша карта Tangem предназначена для работы с другим приложением. Пожалуйста, ознакомьтесь с названием и инструкциями на вашей карте и установите правильное приложение.</string>
|
||||
<string name="wallet_network_group_title">Сеть %s</string>
|
||||
<string name="wallet_notification_address_copied">Адрес скопирован в буфер обмена</string>
|
||||
<string name="wallet_notification_no_internet">Нет соединения с интернетом</string>
|
||||
<string name="wallet_pending_tx_receiving">Получение</string>
|
||||
|
|
|
|||
|
|
@ -465,6 +465,7 @@
|
|||
<string name="wallet_error_no_account">Account is not created</string>
|
||||
<string name="wallet_error_unsupported_blockchain">This card is not supported</string>
|
||||
<string name="wallet_error_unsupported_blockchain_subtitle">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</string>
|
||||
<string name="wallet_network_group_title">%s network</string>
|
||||
<string name="wallet_notification_address_copied">Address was copied to clipboard</string>
|
||||
<string name="wallet_notification_no_internet">No internet connection</string>
|
||||
<string name="wallet_pending_tx_receiving">Receiving</string>
|
||||
|
|
|
|||
|
|
@ -1,34 +1,14 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
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
|
||||
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
|
||||
|
|
@ -59,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,
|
||||
|
|
@ -69,7 +49,7 @@ fun TextButtonIconLeft(
|
|||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Left(icon),
|
||||
icon = TangemButtonIcon.Start(iconResId),
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
showProgress = false,
|
||||
|
|
@ -117,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,
|
||||
|
|
@ -128,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,
|
||||
|
|
@ -140,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,
|
||||
|
|
@ -151,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,
|
||||
|
|
@ -184,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,
|
||||
|
|
@ -195,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,
|
||||
|
|
@ -207,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,
|
||||
|
|
@ -218,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,
|
||||
|
|
@ -234,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,
|
||||
|
|
@ -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,
|
||||
@DrawableRes iconResId: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Start(iconResId),
|
||||
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,
|
||||
@DrawableRes iconResId: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Start(iconResId),
|
||||
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,
|
||||
@DrawableRes iconResId: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
TangemButton(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
icon = TangemButtonIcon.Start(iconResId),
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
showProgress = false,
|
||||
colors = TangemButtonsDefaults.backgroundButtonColors,
|
||||
size = TangemButtonSize.RoundedAction,
|
||||
)
|
||||
}
|
||||
// endregion Action
|
||||
|
||||
// region Defaults
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
|
|
@ -272,6 +324,7 @@ private fun TangemButton(
|
|||
elevation = elevation,
|
||||
shape = size.toShape(),
|
||||
colors = colors,
|
||||
contentPadding = size.toContentPadding(icon = icon),
|
||||
) {
|
||||
ButtonContent(
|
||||
text = text,
|
||||
|
|
@ -296,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,
|
||||
)
|
||||
|
|
@ -320,63 +373,132 @@ 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface TangemButtonIcon {
|
||||
val painter: Painter?
|
||||
@Immutable
|
||||
private sealed interface TangemButtonIcon {
|
||||
val iconResId: Int?
|
||||
|
||||
@Immutable
|
||||
data class Left(override val painter: Painter) : TangemButtonIcon
|
||||
data class Start(override val iconResId: Int) : TangemButtonIcon
|
||||
|
||||
@Immutable
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
enum class TangemButtonSize {
|
||||
private enum class TangemButtonSize {
|
||||
Default,
|
||||
Text,
|
||||
Selector,
|
||||
Action,
|
||||
RoundedAction,
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
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
|
||||
@ReadOnlyComposable
|
||||
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
|
||||
@ReadOnlyComposable
|
||||
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 {
|
||||
@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<Dp, Dp> {
|
||||
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(
|
||||
|
|
@ -385,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,
|
||||
|
|
@ -393,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,
|
||||
|
|
@ -401,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,
|
||||
|
|
@ -409,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,
|
||||
|
|
@ -417,12 +547,24 @@ object TangemButtonsDefaults {
|
|||
)
|
||||
|
||||
val selectorButtonColors: ButtonColors
|
||||
@Composable get() = TangemButtonColors(
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = TangemButtonColors(
|
||||
backgroundColor = Color.Transparent,
|
||||
contentColor = TangemTheme.colors.text.tertiary,
|
||||
disabledBackgroundColor = Color.Transparent,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
|
||||
val backgroundButtonColors: ButtonColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = TangemButtonColors(
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
contentColor = TangemTheme.colors.text.primary1,
|
||||
disabledBackgroundColor = TangemTheme.colors.button.disabled,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
|
|
@ -464,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))
|
||||
|
|
@ -485,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 */ },
|
||||
)
|
||||
|
|
@ -538,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))
|
||||
|
|
@ -559,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 */ },
|
||||
)
|
||||
|
|
@ -604,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))
|
||||
|
|
@ -616,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 */ },
|
||||
)
|
||||
|
|
@ -650,4 +792,67 @@ private fun TextButtonPreview_Dark() {
|
|||
TextButtonSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActionButtonSample(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
RoundedActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
ActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
BackgroundActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
RoundedActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
enabled = false,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
ActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
enabled = false,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
|
||||
BackgroundActionButton(
|
||||
text = "Send",
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
enabled = false,
|
||||
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
|
|
|
|||
10
core/ui/src/main/res/drawable/ic_group_drop_24.xml
Normal file
10
core/ui/src/main/res/drawable/ic_group_drop_24.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M7,8H5V10H7V8ZM13,8H11V10H13V8ZM17,8H19V10H17V8ZM7,14H5V16H7V14ZM11,14H13V16H11V14ZM19,14H17V16H19V14Z" />
|
||||
</vector>
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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<NetworkGroupState>(
|
||||
collection = listOf(
|
||||
WalletPreviewData.networkGroup,
|
||||
WalletPreviewData.draggableNetworkGroup,
|
||||
),
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -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<TokenItemState>(
|
||||
collection = listOf(
|
||||
WalletPreviewData.tokenItemVisibleState,
|
||||
WalletPreviewData.tokenItemUnreachableState,
|
||||
WalletPreviewData.tokenItemDragState,
|
||||
WalletPreviewData.tokenItemHiddenState,
|
||||
WalletPreviewData.loadingTokenItemState,
|
||||
),
|
||||
)
|
||||
|
||||
// endregion preview
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<NetworkGroupState>,
|
||||
) : TokenListState
|
||||
|
||||
data class Ungrouped(
|
||||
val tokens: ImmutableList<TokenItemState>,
|
||||
) : TokenListState
|
||||
}
|
||||
|
||||
@Immutable
|
||||
internal sealed interface NetworkGroupState {
|
||||
val id: String
|
||||
val networkName: String
|
||||
val tokens: ImmutableList<TokenItemState>
|
||||
|
||||
data class Draggable(
|
||||
override val id: String,
|
||||
override val networkName: String,
|
||||
override val tokens: ImmutableList<TokenItemState.Draggable>,
|
||||
) : NetworkGroupState
|
||||
|
||||
data class Content(
|
||||
override val id: String,
|
||||
override val networkName: String,
|
||||
override val tokens: ImmutableList<TokenItemState>,
|
||||
) : NetworkGroupState
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.wallet.presentation.state
|
||||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
|
|
@ -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<WalletCardState>,
|
||||
val wallets: ImmutableList<WalletCardState>,
|
||||
val onScanCardClick: () -> Unit,
|
||||
val onMoreClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
|
|
@ -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<WalletStateHolder.HeaderConfig>(
|
||||
collection = listOf(
|
||||
WalletStateHolder.HeaderConfig(
|
||||
wallets = listOf(
|
||||
wallets = persistentListOf(
|
||||
WalletPreviewData.walletCardContent,
|
||||
WalletPreviewData.walletCardLoading,
|
||||
WalletPreviewData.walletCardHiddenContent,
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ import org.gradle.api.Project
|
|||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
class ConfigurationPlugin : Plugin<Project> {
|
||||
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<Plugin<*>> {
|
||||
project.configure()
|
||||
private fun startPluginConfigurationAction(project: Project) = Action<Plugin<*>> {
|
||||
when (this) {
|
||||
is AppPlugin -> {
|
||||
project.configure<AppExtension> { configure(project) }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue