Updated on 2026-08-14
This commit is contained in:
commit
d617c9c30d
491 changed files with 15019 additions and 5050 deletions
|
|
@ -5,13 +5,15 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.arkivanov.decompose.defaultComponentContext
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.routing.utils.asRouter
|
||||
import com.tangem.core.decompose.context.DefaultAppComponentContext
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.features.markets.MarketsFeatureToggles
|
||||
import com.tangem.features.markets.component.MarketsEntryComponent
|
||||
import com.tangem.features.markets.entry.MarketsEntryComponent
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
|
@ -44,6 +46,9 @@ internal class WalletFragment : ComposeFragment() {
|
|||
@Inject
|
||||
internal lateinit var marketsFeatureToggles: MarketsFeatureToggles
|
||||
|
||||
@Inject
|
||||
internal lateinit var appRouter: AppRouter
|
||||
|
||||
private var marketsEntryComponent: MarketsEntryComponent? = null
|
||||
|
||||
private val _walletRouter: InnerWalletRouter
|
||||
|
|
@ -60,6 +65,7 @@ internal class WalletFragment : ComposeFragment() {
|
|||
messageHandler = uiDependencies.eventMessageHandler,
|
||||
dispatchers = coroutineDispatcherProvider,
|
||||
hiltComponentBuilder = componentBuilder,
|
||||
replaceRouter = appRouter.asRouter(),
|
||||
)
|
||||
|
||||
marketsEntryComponent = marketsEntryComponentFactory.create(appContext)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@ package com.tangem.feature.wallet.presentation.common
|
|||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
|
||||
|
|
@ -66,122 +65,22 @@ internal object WalletPreviewData {
|
|||
)
|
||||
}
|
||||
|
||||
val coinIconState
|
||||
get() = CurrencyIconState.CoinIcon(
|
||||
url = null,
|
||||
fallbackResId = R.drawable.img_polygon_22,
|
||||
isGrayscale = false,
|
||||
showCustomBadge = false,
|
||||
)
|
||||
|
||||
private val tokenIconState
|
||||
get() = CurrencyIconState.TokenIcon(
|
||||
url = null,
|
||||
topBadgeIconResId = R.drawable.img_polygon_22,
|
||||
fallbackTint = TangemColorPalette.Black,
|
||||
fallbackBackground = TangemColorPalette.Meadow,
|
||||
isGrayscale = false,
|
||||
showCustomBadge = false,
|
||||
)
|
||||
|
||||
private val customTokenIconState
|
||||
get() = CurrencyIconState.CustomTokenIcon(
|
||||
tint = TangemColorPalette.Black,
|
||||
background = TangemColorPalette.Meadow,
|
||||
topBadgeIconResId = R.drawable.img_polygon_22,
|
||||
isGrayscale = false,
|
||||
)
|
||||
|
||||
val tokenItemVisibleState by lazy {
|
||||
TokenItemState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
iconState = coinIconState,
|
||||
titleState = TokenItemState.TitleState.Content(text = "Polygon", hasPending = true),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "321 $", hasStaked = true),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = "5,412 MATIC"),
|
||||
cryptoPriceState = TokenItemState.CryptoPriceState.Unknown,
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
val testnetTokenItemVisibleState by lazy {
|
||||
tokenItemVisibleState.copy(
|
||||
titleState = TokenItemState.TitleState.Content(text = "Polygon testnet"),
|
||||
iconState = tokenIconState.copy(isGrayscale = true),
|
||||
)
|
||||
}
|
||||
|
||||
val tokenItemHiddenState by lazy {
|
||||
TokenItemState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
iconState = tokenIconState,
|
||||
titleState = TokenItemState.TitleState.Content(text = "Polygon"),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "321 $", hasStaked = false),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = "5,412 MATIC"),
|
||||
cryptoPriceState = TokenItemState.CryptoPriceState.Content(
|
||||
price = "312 USD",
|
||||
priceChangePercent = "2.0%",
|
||||
type = PriceChangeType.UP,
|
||||
),
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
val tokenItemDragState by lazy {
|
||||
private val tokenItemDragState by lazy {
|
||||
TokenItemState.Draggable(
|
||||
id = UUID.randomUUID().toString(),
|
||||
iconState = tokenIconState,
|
||||
iconState = CurrencyIconState.TokenIcon(
|
||||
url = null,
|
||||
topBadgeIconResId = R.drawable.img_polygon_22,
|
||||
fallbackTint = TangemColorPalette.Black,
|
||||
fallbackBackground = TangemColorPalette.Meadow,
|
||||
isGrayscale = false,
|
||||
showCustomBadge = false,
|
||||
),
|
||||
titleState = TokenItemState.TitleState.Content(text = "Polygon"),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = "3 172,14 $"),
|
||||
)
|
||||
}
|
||||
|
||||
val tokenItemUnreachableState by lazy {
|
||||
TokenItemState.Unreachable(
|
||||
id = UUID.randomUUID().toString(),
|
||||
iconState = tokenIconState,
|
||||
titleState = TokenItemState.TitleState.Content(text = "Polygon"),
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
val tokenItemNoAddressState by lazy {
|
||||
TokenItemState.NoAddress(
|
||||
id = UUID.randomUUID().toString(),
|
||||
iconState = tokenIconState,
|
||||
titleState = TokenItemState.TitleState.Content(text = "Polygon"),
|
||||
onItemLongClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
val customTokenItemVisibleState by lazy {
|
||||
tokenItemVisibleState.copy(
|
||||
titleState = TokenItemState.TitleState.Content(text = "Polygon"),
|
||||
iconState = customTokenIconState.copy(
|
||||
tint = TangemColorPalette.White,
|
||||
background = TangemColorPalette.Black,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val customTestnetTokenItemVisibleState by lazy {
|
||||
tokenItemVisibleState.copy(
|
||||
titleState = TokenItemState.TitleState.Content(text = "Polygon"),
|
||||
iconState = customTokenIconState.copy(isGrayscale = true),
|
||||
)
|
||||
}
|
||||
|
||||
val loadingTokenItemState by lazy {
|
||||
TokenItemState.Loading(
|
||||
id = "Loading#1",
|
||||
iconState = customTokenIconState.copy(isGrayscale = true),
|
||||
titleState = TokenItemState.TitleState.Content(text = "Polygon"),
|
||||
)
|
||||
}
|
||||
|
||||
private const val networksSize = 10
|
||||
private const val tokensSize = 3
|
||||
private val draggableItems by lazy {
|
||||
|
|
|
|||
|
|
@ -56,30 +56,46 @@ internal fun DraggableNetworkGroupItem(
|
|||
)
|
||||
}
|
||||
|
||||
// TODO: [REDACTED_JIRA]
|
||||
@Composable
|
||||
private fun InternalNetworkGroupItem(
|
||||
networkName: String,
|
||||
modifier: Modifier = Modifier,
|
||||
endIcon: @Composable RowScope.() -> Unit = {},
|
||||
endIcon: @Composable (RowScope.() -> Unit)? = null,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12)
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size40),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.wallet_network_group_title, networkName),
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing12, bottom = TangemTheme.dimens.spacing8),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
endIcon()
|
||||
}
|
||||
val minHeight = if (endIcon == null) TangemTheme.dimens.size36 else TangemTheme.dimens.size40
|
||||
val padding = if (endIcon == null) {
|
||||
PaddingValues(
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
top = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing12,
|
||||
bottom = TangemTheme.dimens.spacing4,
|
||||
)
|
||||
} else {
|
||||
PaddingValues(
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
top = TangemTheme.dimens.spacing11,
|
||||
end = TangemTheme.dimens.spacing12,
|
||||
bottom = TangemTheme.dimens.spacing5,
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = minHeight)
|
||||
.padding(paddingValues = padding),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.wallet_network_group_title, networkName),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
|
||||
if (endIcon != null) endIcon()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,426 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.common.component
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.layout.Measurable
|
||||
import androidx.compose.ui.layout.Placeable
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
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.Constraints
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.rememberHapticFeedback
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.component.token.*
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import kotlin.math.max
|
||||
|
||||
private const val TITLE_MIN_WIDTH_COEFFICIENT = 0.3
|
||||
private const val PRICE_MIN_WIDTH_COEFFICIENT = 0.32
|
||||
|
||||
private enum class LayoutId {
|
||||
ICON, TITLE, FIAT_AMOUNT, CRYPTO_AMOUNT, CRYPTO_PRICE, NON_FIAT_CONTENT
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun TokenItem(
|
||||
state: TokenItemState,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
reorderableTokenListState: ReorderableLazyListState? = null,
|
||||
) {
|
||||
val betweenRowsMargin = TangemTheme.dimens.spacing2
|
||||
|
||||
CustomContainer(
|
||||
state = state,
|
||||
modifier = modifier
|
||||
.tokenClickable(state = state)
|
||||
.background(color = TangemTheme.colors.background.primary),
|
||||
) {
|
||||
CurrencyIcon(
|
||||
state = state.iconState,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.ICON)
|
||||
.padding(end = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
|
||||
TokenTitle(
|
||||
state = state.titleState,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.TITLE)
|
||||
.padding(end = TangemTheme.dimens.spacing8)
|
||||
.padding(bottom = betweenRowsMargin),
|
||||
)
|
||||
|
||||
TokenFiatAmount(
|
||||
state = state.fiatAmountState,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.FIAT_AMOUNT)
|
||||
.padding(bottom = betweenRowsMargin),
|
||||
)
|
||||
|
||||
TokenPrice(
|
||||
state = state.cryptoPriceState,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.CRYPTO_PRICE)
|
||||
.padding(end = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
|
||||
TokenCryptoAmount(
|
||||
state = state.cryptoAmountState,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier.layoutId(layoutId = LayoutId.CRYPTO_AMOUNT),
|
||||
)
|
||||
|
||||
NonFiatContentBlock(
|
||||
state = state,
|
||||
reorderableTokenListState = reorderableTokenListState,
|
||||
modifier = Modifier.layoutId(layoutId = LayoutId.NON_FIAT_CONTENT),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
private fun Modifier.tokenClickable(state: TokenItemState): Modifier = composed {
|
||||
when (state) {
|
||||
is TokenItemState.Content -> {
|
||||
val onLongClick = rememberHapticFeedback(state = state, onAction = state.onItemLongClick)
|
||||
combinedClickable(onClick = state.onItemClick, onLongClick = onLongClick)
|
||||
}
|
||||
is TokenItemState.Unreachable -> {
|
||||
val onLongClick = rememberHapticFeedback(state = state, onAction = state.onItemLongClick)
|
||||
combinedClickable(onClick = state.onItemClick, onLongClick = onLongClick)
|
||||
}
|
||||
is TokenItemState.NoAddress -> {
|
||||
val onLongClick = rememberHapticFeedback(state = state, onAction = state.onItemLongClick)
|
||||
combinedClickable(onClick = {}, onLongClick = onLongClick)
|
||||
}
|
||||
is TokenItemState.Draggable,
|
||||
is TokenItemState.Loading,
|
||||
is TokenItemState.Locked,
|
||||
-> this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORTANT! All margins that used between children setup like as children paddings.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier, content: @Composable () -> Unit) {
|
||||
val density = LocalDensity.current
|
||||
val dimens = TangemTheme.dimens
|
||||
|
||||
Layout(content = content, modifier = modifier) { measurables, constraints ->
|
||||
|
||||
val layoutWidth = constraints.maxWidth
|
||||
val horizontalPadding = with(density) { dimens.size12.roundToPx() }
|
||||
val verticalPadding = with(density) { dimens.size15.roundToPx() }
|
||||
val layoutWidthWithoutPaddings = layoutWidth - 2 * horizontalPadding
|
||||
|
||||
val titleMinWidth = (layoutWidth * TITLE_MIN_WIDTH_COEFFICIENT).toInt()
|
||||
val priceChangeMinWidth = (layoutWidth * PRICE_MIN_WIDTH_COEFFICIENT).toInt()
|
||||
|
||||
val icon = measurables.measure(layoutId = LayoutId.ICON, constraints = constraints)
|
||||
|
||||
/*
|
||||
* Title width take the whole REMAINING space.
|
||||
* If FiatAmount took the whole free space, then Title will have min width.
|
||||
*/
|
||||
val title: Placeable
|
||||
|
||||
// FiatAmount width must take the whole free space but is not greater the Title min size
|
||||
var fiatAmount: Placeable? = null
|
||||
|
||||
// CryptoAmount width must take the whole free space but is not greater the PriceChange min size
|
||||
var cryptoAmount: Placeable? = null
|
||||
|
||||
/*
|
||||
* PriceChange width take the whole REMAINING space.
|
||||
* If CryptoAmount took the whole free space, then PriceChange will have min width.
|
||||
*/
|
||||
val priceChange: Placeable?
|
||||
|
||||
val nonFiatContent = measurables.measure(layoutId = LayoutId.NON_FIAT_CONTENT, constraints = constraints)
|
||||
|
||||
var firstRowRemainingFreeSpace: Int? = null
|
||||
var secondRowRemainingFreeSpace: Int? = null
|
||||
|
||||
when (state) {
|
||||
is TokenItemState.Content,
|
||||
is TokenItemState.Loading,
|
||||
is TokenItemState.Locked,
|
||||
-> {
|
||||
fiatAmount = measurables.measureFiatAmount(
|
||||
state = state,
|
||||
maxWidth = layoutWidthWithoutPaddings - icon.width - titleMinWidth,
|
||||
defaultConstraints = constraints,
|
||||
)
|
||||
|
||||
cryptoAmount = measurables.measureCryptoAmount(
|
||||
state = state,
|
||||
maxWidth = layoutWidthWithoutPaddings - icon.width - priceChangeMinWidth,
|
||||
defaultConstraints = constraints,
|
||||
)
|
||||
|
||||
firstRowRemainingFreeSpace = layoutWidthWithoutPaddings - icon.width - fiatAmount.width
|
||||
secondRowRemainingFreeSpace = layoutWidthWithoutPaddings - icon.width - cryptoAmount.width
|
||||
}
|
||||
is TokenItemState.Draggable -> {
|
||||
cryptoAmount = measurables.measureCryptoAmount(
|
||||
state = state,
|
||||
maxWidth = layoutWidthWithoutPaddings - icon.width - nonFiatContent.width,
|
||||
defaultConstraints = constraints,
|
||||
)
|
||||
|
||||
firstRowRemainingFreeSpace = layoutWidthWithoutPaddings - icon.width - nonFiatContent.width
|
||||
}
|
||||
is TokenItemState.NoAddress,
|
||||
is TokenItemState.Unreachable,
|
||||
-> {
|
||||
firstRowRemainingFreeSpace = layoutWidthWithoutPaddings - icon.width - nonFiatContent.width
|
||||
}
|
||||
}
|
||||
|
||||
title = measurables.measureTitle(
|
||||
state = state,
|
||||
minWidth = titleMinWidth,
|
||||
remainingFreeSpace = firstRowRemainingFreeSpace,
|
||||
defaultConstraints = constraints,
|
||||
)
|
||||
|
||||
priceChange = secondRowRemainingFreeSpace?.let {
|
||||
measurables.measurePriceChange(
|
||||
state = state,
|
||||
minWidth = priceChangeMinWidth,
|
||||
remainingFreeSpace = secondRowRemainingFreeSpace,
|
||||
defaultConstraints = constraints,
|
||||
)
|
||||
}
|
||||
|
||||
val layoutHeight = calculateLayoutHeight(
|
||||
state = state,
|
||||
minLayoutHeight = with(density) { dimens.size68.roundToPx() },
|
||||
layoutPadding = verticalPadding,
|
||||
title = title,
|
||||
fiatAmount = fiatAmount,
|
||||
cryptoAmount = cryptoAmount,
|
||||
priceChange = priceChange,
|
||||
)
|
||||
|
||||
layout(width = constraints.maxWidth, height = layoutHeight) {
|
||||
icon.placeRelative(x = horizontalPadding, y = (layoutHeight - icon.height).div(other = 2))
|
||||
|
||||
title.placeRelative(
|
||||
x = horizontalPadding + icon.width,
|
||||
y = when (state) {
|
||||
is TokenItemState.NoAddress,
|
||||
is TokenItemState.Unreachable,
|
||||
-> (layoutHeight - title.height).div(other = 2)
|
||||
else -> verticalPadding
|
||||
},
|
||||
)
|
||||
|
||||
fiatAmount?.placeRelative(x = layoutWidth - fiatAmount.width - horizontalPadding, y = verticalPadding)
|
||||
|
||||
priceChange?.placeRelative(
|
||||
x = horizontalPadding + icon.width,
|
||||
y = layoutHeight - priceChange.height - verticalPadding,
|
||||
)
|
||||
|
||||
cryptoAmount?.placeRelative(
|
||||
x = when (state) {
|
||||
is TokenItemState.Draggable -> horizontalPadding + icon.width
|
||||
else -> layoutWidth - cryptoAmount.width - horizontalPadding
|
||||
},
|
||||
y = layoutHeight - cryptoAmount.height - verticalPadding,
|
||||
)
|
||||
|
||||
nonFiatContent.placeRelative(
|
||||
x = layoutWidth - nonFiatContent.width - horizontalPadding,
|
||||
y = (layoutHeight - nonFiatContent.height).div(other = 2),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Measurable>.measureFiatAmount(
|
||||
state: TokenItemState,
|
||||
maxWidth: Int,
|
||||
defaultConstraints: Constraints,
|
||||
): Placeable {
|
||||
return measure(
|
||||
layoutId = LayoutId.FIAT_AMOUNT,
|
||||
constraints = when (state) {
|
||||
is TokenItemState.Content,
|
||||
is TokenItemState.Draggable,
|
||||
-> createConstrainsSafely(maxWidth = maxWidth)
|
||||
else -> defaultConstraints
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<Measurable>.measureCryptoAmount(
|
||||
state: TokenItemState,
|
||||
maxWidth: Int,
|
||||
defaultConstraints: Constraints,
|
||||
): Placeable {
|
||||
return measure(
|
||||
layoutId = LayoutId.CRYPTO_AMOUNT,
|
||||
constraints = when (state) {
|
||||
is TokenItemState.Content,
|
||||
is TokenItemState.Draggable,
|
||||
-> createConstrainsSafely(maxWidth = maxWidth)
|
||||
else -> defaultConstraints
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<Measurable>.measureTitle(
|
||||
state: TokenItemState,
|
||||
minWidth: Int,
|
||||
remainingFreeSpace: Int,
|
||||
defaultConstraints: Constraints,
|
||||
): Placeable {
|
||||
return measure(
|
||||
layoutId = LayoutId.TITLE,
|
||||
constraints = when (state) {
|
||||
is TokenItemState.Content,
|
||||
is TokenItemState.Draggable,
|
||||
is TokenItemState.NoAddress,
|
||||
is TokenItemState.Unreachable,
|
||||
-> createDynamicConstrains(minWidth = minWidth, remainingFreeSpace = remainingFreeSpace)
|
||||
else -> defaultConstraints
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<Measurable>.measurePriceChange(
|
||||
state: TokenItemState,
|
||||
minWidth: Int,
|
||||
remainingFreeSpace: Int,
|
||||
defaultConstraints: Constraints,
|
||||
): Placeable {
|
||||
return measure(
|
||||
layoutId = LayoutId.CRYPTO_PRICE,
|
||||
constraints = when (state) {
|
||||
is TokenItemState.Content,
|
||||
-> createDynamicConstrains(minWidth = minWidth, remainingFreeSpace = remainingFreeSpace)
|
||||
else -> defaultConstraints
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<Measurable>.measure(layoutId: LayoutId, constraints: Constraints): Placeable {
|
||||
return requireNotNull(
|
||||
value = firstOrNull { it.layoutId == layoutId },
|
||||
lazyMessage = { "Measurables[$layoutId] is null" },
|
||||
).measure(constraints)
|
||||
}
|
||||
|
||||
private fun createDynamicConstrains(minWidth: Int, remainingFreeSpace: Int): Constraints {
|
||||
return createConstrainsSafely(
|
||||
minWidth = minWidth,
|
||||
maxWidth = max(a = minWidth, b = remainingFreeSpace),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createConstrainsSafely(
|
||||
minWidth: Int = 0,
|
||||
maxWidth: Int = Constraints.Infinity,
|
||||
minHeight: Int = 0,
|
||||
maxHeight: Int = Constraints.Infinity,
|
||||
): Constraints {
|
||||
return Constraints(
|
||||
minWidth = minWidth.makeNotLessZero(),
|
||||
maxWidth = maxWidth.makeNotLessZero(),
|
||||
minHeight = minHeight.makeNotLessZero(),
|
||||
maxHeight = maxHeight.makeNotLessZero(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun Int.makeNotLessZero(): Int = max(a = 0, b = this)
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
private fun calculateLayoutHeight(
|
||||
state: TokenItemState,
|
||||
minLayoutHeight: Int,
|
||||
layoutPadding: Int,
|
||||
title: Placeable,
|
||||
fiatAmount: Placeable?,
|
||||
cryptoAmount: Placeable?,
|
||||
priceChange: Placeable?,
|
||||
): Int {
|
||||
val firstColumnHeight: Int
|
||||
val secondColumnHeight: Int
|
||||
|
||||
when (state) {
|
||||
is TokenItemState.Content,
|
||||
is TokenItemState.Loading,
|
||||
is TokenItemState.Locked,
|
||||
-> {
|
||||
firstColumnHeight = 2 * layoutPadding + title.height + (cryptoAmount?.height ?: 0)
|
||||
secondColumnHeight = 2 * layoutPadding + (fiatAmount?.height ?: 0) + (priceChange?.height ?: 0)
|
||||
}
|
||||
is TokenItemState.Draggable,
|
||||
is TokenItemState.NoAddress,
|
||||
is TokenItemState.Unreachable,
|
||||
-> {
|
||||
firstColumnHeight = minLayoutHeight
|
||||
secondColumnHeight = minLayoutHeight
|
||||
}
|
||||
}
|
||||
|
||||
return max(firstColumnHeight, secondColumnHeight).coerceAtLeast(minLayoutHeight)
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360)
|
||||
@Composable
|
||||
private fun Preview_TokenItem_InLight(@PreviewParameter(TokenItemStateProvider::class) state: TokenItemState) {
|
||||
TangemThemePreview(isDark = false) {
|
||||
TokenItem(state = state, isBalanceHidden = false)
|
||||
}
|
||||
}
|
||||
|
||||
private class TokenItemStateProvider : CollectionPreviewParameterProvider<TokenItemState>(
|
||||
collection = listOf(
|
||||
WalletPreviewData.tokenItemVisibleState.copy(
|
||||
iconState = WalletPreviewData.coinIconState.copy(showCustomBadge = true),
|
||||
titleState = TokenItemState.TitleState.Content(
|
||||
text = "PolygonPolygonPolygonPolygonPolygonPolygon",
|
||||
hasPending = true,
|
||||
),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(
|
||||
text = "3213123123321312312312312312 $",
|
||||
hasStaked = true,
|
||||
),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = "5,4123123213123123123123123123 MATIC"),
|
||||
cryptoPriceState = TokenItemState.CryptoPriceState.Content(
|
||||
price = "312 USD",
|
||||
priceChangePercent = "42.0%",
|
||||
type = PriceChangeType.UP,
|
||||
),
|
||||
),
|
||||
WalletPreviewData.tokenItemUnreachableState,
|
||||
WalletPreviewData.tokenItemNoAddressState,
|
||||
WalletPreviewData.tokenItemDragState,
|
||||
WalletPreviewData.tokenItemHiddenState,
|
||||
WalletPreviewData.loadingTokenItemState,
|
||||
WalletPreviewData.testnetTokenItemVisibleState,
|
||||
WalletPreviewData.customTokenItemVisibleState,
|
||||
WalletPreviewData.customTestnetTokenItemVisibleState,
|
||||
),
|
||||
)
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.common.component.token
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun LockedRectangle(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.background(
|
||||
color = TangemTheme.colors.field.primary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius4),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.common.component.token
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.detectReorder
|
||||
|
||||
@Composable
|
||||
internal fun NonFiatContentBlock(
|
||||
state: TokenItemState,
|
||||
reorderableTokenListState: ReorderableLazyListState?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
AnimatedContent(
|
||||
targetState = state,
|
||||
label = "Update non content fiat block",
|
||||
modifier = modifier,
|
||||
contentKey = { it::class.java },
|
||||
) { animatedState ->
|
||||
when (animatedState) {
|
||||
is TokenItemState.Draggable -> DraggableImage(reorderableTokenListState = reorderableTokenListState)
|
||||
is TokenItemState.Unreachable -> NonFiatContentText(text = R.string.common_unreachable)
|
||||
is TokenItemState.NoAddress -> NonFiatContentText(text = R.string.common_no_address)
|
||||
is TokenItemState.Content,
|
||||
is TokenItemState.Loading,
|
||||
is TokenItemState.Locked,
|
||||
-> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DraggableImage(reorderableTokenListState: ReorderableLazyListState?) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(size = TangemTheme.dimens.size32)
|
||||
.then(
|
||||
other = if (reorderableTokenListState != null) {
|
||||
Modifier.detectReorder(reorderableTokenListState)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_drag_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NonFiatContentText(@StringRes text: Int) {
|
||||
Text(
|
||||
text = stringResource(id = text),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.common.component.token
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.CryptoAmountState as TokenCryptoAmountState
|
||||
|
||||
@Composable
|
||||
internal fun TokenCryptoAmount(
|
||||
state: TokenCryptoAmountState?,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (state) {
|
||||
is TokenCryptoAmountState.Content -> {
|
||||
CryptoAmountText(
|
||||
amount = if (isBalanceHidden) StringsSigns.STARS else state.text,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
is TokenCryptoAmountState.Unreachable -> {
|
||||
CryptoAmountText(amount = stringResource(id = R.string.common_unreachable), modifier = modifier)
|
||||
}
|
||||
is TokenCryptoAmountState.Loading -> {
|
||||
RectangleShimmer(modifier = modifier.placeholderSize(), radius = TangemTheme.dimens.radius4)
|
||||
}
|
||||
is TokenCryptoAmountState.Locked -> {
|
||||
LockedRectangle(modifier = modifier.placeholderSize())
|
||||
}
|
||||
null -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CryptoAmountText(amount: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = amount,
|
||||
modifier = modifier,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
|
||||
private fun Modifier.placeholderSize(): Modifier = composed {
|
||||
return@composed this
|
||||
.padding(vertical = TangemTheme.dimens.spacing2)
|
||||
.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12)
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.common.component.token
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.FiatAmountState as TokenFiatAmountState
|
||||
|
||||
@Composable
|
||||
internal fun TokenFiatAmount(state: TokenFiatAmountState?, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TokenFiatAmountState.Content -> {
|
||||
FiatAmountText(
|
||||
text = if (isBalanceHidden) StringsSigns.STARS else state.text,
|
||||
hasStaked = state.hasStaked,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
is TokenFiatAmountState.Loading -> {
|
||||
RectangleShimmer(modifier = modifier.placeholderSize(), radius = TangemTheme.dimens.radius4)
|
||||
}
|
||||
is TokenFiatAmountState.Locked -> {
|
||||
LockedRectangle(modifier = modifier.placeholderSize())
|
||||
}
|
||||
null -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FiatAmountText(text: String, hasStaked: Boolean, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = hasStaked,
|
||||
) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(image = ImageVector.vectorResource(R.drawable.ic_staking_24)),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing4)
|
||||
.size(TangemTheme.dimens.size12),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = text,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.placeholderSize(): Modifier = composed {
|
||||
return@composed this
|
||||
.padding(vertical = TangemTheme.dimens.spacing4)
|
||||
.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12)
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.common.component.token
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerW4
|
||||
import com.tangem.core.ui.components.SpacerW6
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.CryptoPriceState as TokenPriceChangeState
|
||||
|
||||
@Composable
|
||||
internal fun TokenPrice(state: TokenPriceChangeState?, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TokenPriceChangeState.Content -> {
|
||||
PriceBlock(
|
||||
modifier = modifier,
|
||||
price = state.price,
|
||||
type = state.type,
|
||||
priceChangePercent = state.priceChangePercent,
|
||||
)
|
||||
}
|
||||
is TokenPriceChangeState.Unknown -> {
|
||||
PriceText(text = DASH_SIGN, modifier = modifier)
|
||||
}
|
||||
is TokenPriceChangeState.Loading -> {
|
||||
RectangleShimmer(modifier = modifier.placeholderSize(), radius = TangemTheme.dimens.radius4)
|
||||
}
|
||||
is TokenPriceChangeState.Locked -> {
|
||||
LockedRectangle(modifier = modifier.placeholderSize())
|
||||
}
|
||||
null -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PriceBlock(
|
||||
price: String,
|
||||
modifier: Modifier = Modifier,
|
||||
type: PriceChangeType? = null,
|
||||
priceChangePercent: String? = null,
|
||||
) {
|
||||
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
|
||||
PriceText(text = price, modifier = Modifier.weight(weight = 1f, fill = false))
|
||||
|
||||
SpacerW6()
|
||||
|
||||
if (type != null) {
|
||||
PriceChangeIcon(type = type)
|
||||
SpacerW4()
|
||||
}
|
||||
|
||||
PriceChangeText(type = type, text = priceChangePercent)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PriceText(text: String, modifier: Modifier = Modifier) {
|
||||
AnimatedContent(targetState = text, label = "Update the price text", modifier = modifier) { animatedText ->
|
||||
Text(
|
||||
text = animatedText,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PriceChangeIcon(type: PriceChangeType) {
|
||||
AnimatedContent(targetState = type, label = "Update the price change's arrow") { animatedType ->
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
id = when (animatedType) {
|
||||
PriceChangeType.UP -> R.drawable.ic_arrow_up_8
|
||||
PriceChangeType.DOWN -> R.drawable.ic_arrow_down_8
|
||||
PriceChangeType.NEUTRAL -> R.drawable.ic_elipse_8
|
||||
},
|
||||
),
|
||||
tint = when (animatedType) {
|
||||
PriceChangeType.UP -> TangemTheme.colors.icon.accent
|
||||
PriceChangeType.DOWN -> TangemTheme.colors.icon.warning
|
||||
PriceChangeType.NEUTRAL -> TangemTheme.colors.icon.inactive
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PriceChangeText(type: PriceChangeType?, text: String?, modifier: Modifier = Modifier) {
|
||||
AnimatedContent(targetState = text, modifier = modifier, label = "Update the price change's text") { animatedText ->
|
||||
Text(
|
||||
text = animatedText ?: DASH_SIGN,
|
||||
color = when (type) {
|
||||
PriceChangeType.UP -> TangemTheme.colors.text.accent
|
||||
PriceChangeType.DOWN -> TangemTheme.colors.text.warning
|
||||
PriceChangeType.NEUTRAL -> TangemTheme.colors.text.disabled
|
||||
null -> TangemTheme.colors.text.tertiary
|
||||
},
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.placeholderSize(): Modifier = composed {
|
||||
return@composed this
|
||||
.padding(vertical = TangemTheme.dimens.spacing2)
|
||||
.size(width = TangemTheme.dimens.size52, height = TangemTheme.dimens.size12)
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.common.component.token
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TitleState as TokenTitleState
|
||||
|
||||
@Composable
|
||||
internal fun TokenTitle(state: TokenTitleState?, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TokenTitleState.Content -> {
|
||||
ContentTitle(name = state.text, hasPending = state.hasPending, modifier = modifier)
|
||||
}
|
||||
is TokenTitleState.Loading -> {
|
||||
RectangleShimmer(modifier = modifier.placeholderSize(), radius = TangemTheme.dimens.radius4)
|
||||
}
|
||||
is TokenTitleState.Locked -> {
|
||||
LockedRectangle(modifier = modifier.placeholderSize())
|
||||
}
|
||||
null -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentTitle(name: String, hasPending: Boolean, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing6),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
/*
|
||||
* If currency name has a long width, then it will completely displace the image.
|
||||
* So we need to use [weight] to avoid displacement.
|
||||
*/
|
||||
CurrencyNameText(name = name, modifier = Modifier.weight(weight = 1f, fill = false))
|
||||
|
||||
PendingTransactionImage(
|
||||
hasPending = hasPending,
|
||||
modifier = Modifier.align(alignment = Alignment.CenterVertically),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CurrencyNameText(name: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = name,
|
||||
modifier = modifier,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PendingTransactionImage(hasPending: Boolean, modifier: Modifier = Modifier) {
|
||||
AnimatedVisibility(visible = hasPending, modifier = modifier) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.img_loader_15),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.placeholderSize(): Modifier = composed {
|
||||
return@composed this
|
||||
.padding(vertical = TangemTheme.dimens.spacing4)
|
||||
.size(width = TangemTheme.dimens.size70, height = TangemTheme.dimens.size12)
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.common.preview
|
|||
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
|
|
@ -9,7 +10,6 @@ import com.tangem.core.ui.utils.BigDecimalFormatter
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData.topBarConfig
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ internal object WalletScreenPreviewData {
|
|||
titleState = TokenItemState.TitleState.Content(text = "Bitcoin"),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "12 368,14 \$"),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = "0,35853044 BTC"),
|
||||
cryptoPriceState = TokenItemState.CryptoPriceState.Content(
|
||||
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
|
||||
price = "34 496,75 \$",
|
||||
priceChangePercent = "0,43 %",
|
||||
type = PriceChangeType.DOWN,
|
||||
|
|
@ -46,7 +46,7 @@ internal object WalletScreenPreviewData {
|
|||
titleState = TokenItemState.TitleState.Content(text = "Ethereum"),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "3 340,79 \$"),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = "1,856660295 ETH"),
|
||||
cryptoPriceState = TokenItemState.CryptoPriceState.Content(
|
||||
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
|
||||
price = "1 799,41 \$",
|
||||
priceChangePercent = "5,16 %",
|
||||
type = PriceChangeType.UP,
|
||||
|
|
@ -68,7 +68,7 @@ internal object WalletScreenPreviewData {
|
|||
titleState = TokenItemState.TitleState.Content(text = "Shiba Inu"),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "48,64 \$"),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = "6 200 220,00 SHIB"),
|
||||
cryptoPriceState = TokenItemState.CryptoPriceState.Content(
|
||||
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
|
||||
price = "0.01 \$",
|
||||
priceChangePercent = "1,34 %",
|
||||
type = PriceChangeType.DOWN,
|
||||
|
|
|
|||
|
|
@ -1,167 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.common.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
|
||||
/** Token item state */
|
||||
@Immutable
|
||||
internal sealed class TokenItemState {
|
||||
|
||||
abstract val id: String
|
||||
|
||||
abstract val iconState: CurrencyIconState
|
||||
|
||||
abstract val titleState: TitleState
|
||||
|
||||
abstract val fiatAmountState: FiatAmountState?
|
||||
|
||||
abstract val cryptoAmountState: CryptoAmountState?
|
||||
|
||||
abstract val cryptoPriceState: CryptoPriceState?
|
||||
|
||||
/** Loading token state */
|
||||
data class Loading(
|
||||
override val id: String,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState.Content,
|
||||
) : TokenItemState() {
|
||||
override val fiatAmountState: FiatAmountState = FiatAmountState.Loading
|
||||
override val cryptoAmountState: CryptoAmountState = CryptoAmountState.Loading
|
||||
override val cryptoPriceState: CryptoPriceState = CryptoPriceState.Loading
|
||||
}
|
||||
|
||||
/** Locked token state */
|
||||
data class Locked(override val id: String) : TokenItemState() {
|
||||
override val iconState: CurrencyIconState = CurrencyIconState.Locked
|
||||
override val titleState: TitleState = TitleState.Locked
|
||||
override val fiatAmountState: FiatAmountState = FiatAmountState.Locked
|
||||
override val cryptoAmountState: CryptoAmountState = CryptoAmountState.Locked
|
||||
override val cryptoPriceState: CryptoPriceState = CryptoPriceState.Locked
|
||||
}
|
||||
|
||||
/**
|
||||
* Content token state
|
||||
*
|
||||
* @property id unique id
|
||||
* @property iconState token icon state
|
||||
* @property titleState token name
|
||||
* @property onItemClick callback which will be called when an item is clicked
|
||||
* @property onItemLongClick callback which will be called when an item is long clicked
|
||||
*/
|
||||
data class Content(
|
||||
override val id: String,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState,
|
||||
override val fiatAmountState: FiatAmountState,
|
||||
override val cryptoAmountState: CryptoAmountState.Content,
|
||||
override val cryptoPriceState: CryptoPriceState,
|
||||
val onItemClick: () -> Unit,
|
||||
val onItemLongClick: () -> Unit,
|
||||
) : TokenItemState()
|
||||
|
||||
/**
|
||||
* Draggable token state
|
||||
*
|
||||
* @property id unique id
|
||||
* @property iconState token icon state
|
||||
* @property titleState token name
|
||||
*/
|
||||
data class Draggable(
|
||||
override val id: String,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState,
|
||||
override val cryptoAmountState: CryptoAmountState,
|
||||
) : TokenItemState() {
|
||||
override val fiatAmountState: FiatAmountState? = null
|
||||
override val cryptoPriceState: CryptoPriceState? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Unreachable token state
|
||||
*
|
||||
* @property id token id
|
||||
* @property iconState token icon state
|
||||
* @property titleState token name
|
||||
* @property onItemClick callback which will be called when an item is clicked
|
||||
* @property onItemLongClick callback which will be called when an item is long clicked
|
||||
*/
|
||||
data class Unreachable(
|
||||
override val id: String,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState,
|
||||
val onItemClick: () -> Unit,
|
||||
val onItemLongClick: () -> Unit,
|
||||
) : TokenItemState() {
|
||||
override val fiatAmountState: FiatAmountState? = null
|
||||
override val cryptoAmountState: CryptoAmountState? = null
|
||||
override val cryptoPriceState: CryptoPriceState? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* No derivation address state
|
||||
*
|
||||
* @property id token id
|
||||
* @property iconState token icon state
|
||||
* @property titleState token name
|
||||
* @property onItemLongClick callback which will be called when an item is long clicked
|
||||
*/
|
||||
data class NoAddress(
|
||||
override val id: String,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState,
|
||||
val onItemLongClick: () -> Unit,
|
||||
) : TokenItemState() {
|
||||
override val fiatAmountState: FiatAmountState? = null
|
||||
override val cryptoAmountState: CryptoAmountState? = null
|
||||
override val cryptoPriceState: CryptoPriceState? = null
|
||||
}
|
||||
|
||||
@Immutable
|
||||
sealed class TitleState {
|
||||
|
||||
data class Content(val text: String, val hasPending: Boolean = false) : TitleState()
|
||||
|
||||
object Loading : TitleState()
|
||||
|
||||
object Locked : TitleState()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
sealed class FiatAmountState {
|
||||
data class Content(
|
||||
val text: String,
|
||||
val hasStaked: Boolean = false,
|
||||
) : FiatAmountState()
|
||||
|
||||
object Loading : FiatAmountState()
|
||||
|
||||
object Locked : FiatAmountState()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
sealed class CryptoAmountState {
|
||||
data class Content(val text: String) : CryptoAmountState()
|
||||
|
||||
object Unreachable : CryptoAmountState()
|
||||
|
||||
object Loading : CryptoAmountState()
|
||||
|
||||
object Locked : CryptoAmountState()
|
||||
}
|
||||
|
||||
sealed class CryptoPriceState {
|
||||
|
||||
data class Content(
|
||||
val price: String,
|
||||
val priceChangePercent: String?,
|
||||
val type: PriceChangeType?,
|
||||
) : CryptoPriceState()
|
||||
|
||||
object Unknown : CryptoPriceState()
|
||||
|
||||
object Loading : CryptoPriceState()
|
||||
|
||||
object Locked : CryptoPriceState()
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.AppBarDefaults
|
||||
import androidx.compose.material3.FabPosition
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -32,6 +31,7 @@ import com.tangem.core.ui.components.PrimaryButton
|
|||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
import com.tangem.core.ui.components.buttons.actions.RoundedActionButton
|
||||
import com.tangem.core.ui.components.token.TokenItem
|
||||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -40,7 +40,6 @@ import com.tangem.core.ui.utils.WindowInsetsZero
|
|||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.component.DraggableNetworkGroupItem
|
||||
import com.tangem.feature.wallet.presentation.common.component.TokenItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
|
||||
|
|
@ -181,7 +180,7 @@ private fun LazyItemScope.DraggableItem(
|
|||
reorderableTokenListState = reorderableState,
|
||||
)
|
||||
is DraggableItem.Token -> TokenItem(
|
||||
modifier = itemModifier,
|
||||
modifier = itemModifier.background(color = TangemTheme.colors.background.primary),
|
||||
state = item.tokenItemState,
|
||||
reorderableTokenListState = reorderableState,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
|
|
@ -212,7 +211,7 @@ private fun TopBar(
|
|||
}
|
||||
}
|
||||
val elevation by animateDpAsState(
|
||||
targetValue = if (isElevationEnabled) AppBarDefaults.TopAppBarElevation else TangemTheme.dimens.elevation0,
|
||||
targetValue = if (isElevationEnabled) TangemTheme.dimens.elevation4 else TangemTheme.dimens.elevation0,
|
||||
label = "top_bar_shadow_elevation",
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem.RoundingMode
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items
|
||||
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getTokenItemId
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScree
|
|||
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel
|
||||
import com.tangem.features.markets.component.MarketsEntryComponent
|
||||
import com.tangem.features.markets.entry.MarketsEntryComponent
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/** Default implementation of wallet feature router */
|
||||
|
|
@ -139,8 +139,8 @@ internal class DefaultWalletRouter(
|
|||
return router.stack.lastOrNull() is AppRoute.Wallet
|
||||
}
|
||||
|
||||
override fun openManageTokensScreen() {
|
||||
router.push(AppRoute.ManageTokens(readOnlyContent = false))
|
||||
override fun openManageTokensScreen(userWalletId: UserWalletId) {
|
||||
router.push(AppRoute.ManageTokens(userWalletId = userWalletId))
|
||||
}
|
||||
|
||||
override fun openScanFailedDialog(onTryAgain: () -> Unit) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.markets.component.MarketsEntryComponent
|
||||
import com.tangem.features.markets.entry.MarketsEntryComponent
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
|
||||
/**
|
||||
|
|
@ -54,7 +54,7 @@ internal interface InnerWalletRouter : WalletRouter {
|
|||
fun isWalletLastScreen(): Boolean
|
||||
|
||||
/** Open manage tokens screen */
|
||||
fun openManageTokensScreen()
|
||||
fun openManageTokensScreen(userWalletId: UserWalletId)
|
||||
|
||||
/** Open scan failed dialog */
|
||||
fun openScanFailedDialog(onTryAgain: () -> Unit)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
||||
/**
|
||||
* Note image model
|
||||
*
|
||||
* @property blockchain blockchain
|
||||
* @property imageResId image res id
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal enum class NoteImage(
|
||||
val blockchain: Blockchain,
|
||||
@DrawableRes val imageResId: Int,
|
||||
) {
|
||||
|
||||
Bitcoin(blockchain = Blockchain.Bitcoin, imageResId = R.drawable.ill_note_btc_120_106),
|
||||
|
||||
Ethereum(blockchain = Blockchain.Ethereum, imageResId = R.drawable.ill_note_ethereum_120_106),
|
||||
|
||||
Binance(blockchain = Blockchain.BSC, imageResId = R.drawable.ill_note_binance_120_106),
|
||||
|
||||
Dogecoin(blockchain = Blockchain.Dogecoin, imageResId = R.drawable.ill_note_doge_120_106),
|
||||
|
||||
Cardano(blockchain = Blockchain.Cardano, imageResId = R.drawable.ill_note_cardano_120_106),
|
||||
|
||||
XRP(blockchain = Blockchain.XRP, imageResId = R.drawable.ill_note_xrp_120_106),
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
||||
fun UserWallet.getCardsCount(): Int? {
|
||||
return if (isMultiCurrency) {
|
||||
when (val status = scanResponse.card.backupStatus) {
|
||||
is CardDTO.BackupStatus.Active -> status.cardCount + 1
|
||||
is CardDTO.BackupStatus.NoBackup,
|
||||
is CardDTO.BackupStatus.CardLinked,
|
||||
-> 1
|
||||
null -> 1 // Multi-currency wallet without backup function. Example, 4.12
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
||||
/**
|
||||
* Wallet2 cobrand image.
|
||||
* To integrate a new cobrand, just implement new enum object and that all.
|
||||
*
|
||||
* @property cards2ResId image resource id for wallet set with 2 cards
|
||||
* @property cards3ResId image resource id for wallet set with 3 cards
|
||||
* @property batchIds set of unique batch ids for this cobrand
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal enum class Wallet2CobrandImage(
|
||||
@DrawableRes val cards2ResId: Int,
|
||||
@DrawableRes val cards3ResId: Int,
|
||||
val batchIds: Set<String>,
|
||||
) {
|
||||
|
||||
Avrora(
|
||||
cards2ResId = R.drawable.ill_avrora_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_avrora_card3_120_106,
|
||||
batchIds = setOf("AF18"),
|
||||
),
|
||||
|
||||
BabyDoge(
|
||||
cards2ResId = R.drawable.ill_baby_doge_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_baby_doge_card3_120_106,
|
||||
batchIds = setOf("AF51"),
|
||||
),
|
||||
|
||||
Bad(
|
||||
cards2ResId = R.drawable.ill_bad_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_bad_card3_120_106,
|
||||
batchIds = setOf("AF09"),
|
||||
),
|
||||
|
||||
BitcoinPizzaDay(
|
||||
cards2ResId = R.drawable.ill_pizza_day_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_pizza_day_card3_120_106,
|
||||
batchIds = setOf("AF33"),
|
||||
),
|
||||
|
||||
CoinMetrica(
|
||||
cards2ResId = R.drawable.ill_coin_metrica_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_coin_metrica_card3_120_106,
|
||||
batchIds = setOf("AF27"),
|
||||
),
|
||||
|
||||
COQ(
|
||||
cards2ResId = R.drawable.ill_coq_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_coq_card3_120_106,
|
||||
batchIds = setOf("AF28"),
|
||||
),
|
||||
|
||||
CryptoSeth(
|
||||
cards2ResId = R.drawable.ill_crypto_seth_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_crypto_seth_card3_120_106,
|
||||
batchIds = setOf("AF32"),
|
||||
),
|
||||
|
||||
Grim(
|
||||
cards2ResId = R.drawable.ill_grim_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_grim_card3_120_106,
|
||||
batchIds = setOf("AF13"),
|
||||
),
|
||||
|
||||
Jr(
|
||||
cards2ResId = R.drawable.ill_jr_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_jr_card3_120_106,
|
||||
batchIds = setOf("AF14"),
|
||||
),
|
||||
|
||||
Kaspa(
|
||||
cards2ResId = R.drawable.ill_kaspa_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_kaspa_card3_120_106,
|
||||
batchIds = setOf("AF08"),
|
||||
),
|
||||
|
||||
Kaspa2(
|
||||
cards2ResId = R.drawable.ill_kaspa2_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_kaspa2_card3_120_106,
|
||||
batchIds = setOf("AF25"),
|
||||
),
|
||||
|
||||
KaspaReseller(
|
||||
cards2ResId = R.drawable.ill_kaspa_reseller_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_kaspa_reseller_card3_120_106,
|
||||
batchIds = setOf("AF31"),
|
||||
),
|
||||
|
||||
KishuInu(
|
||||
cards2ResId = R.drawable.ill_kishu_inu_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_kishu_inu_card3_120_106,
|
||||
batchIds = setOf("AF52"),
|
||||
),
|
||||
|
||||
NewWorldElite(
|
||||
cards2ResId = R.drawable.ill_nwe_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_nwe_card3_120_106,
|
||||
batchIds = setOf("AF26"),
|
||||
),
|
||||
|
||||
// for multicolored cards use image of 3 cards in all cases
|
||||
Pastel(
|
||||
cards2ResId = R.drawable.ill_pastel_cards3_120_106,
|
||||
cards3ResId = R.drawable.ill_pastel_cards3_120_106,
|
||||
batchIds = setOf("AF43", "AF44", "AF45"),
|
||||
),
|
||||
|
||||
RedPanda(
|
||||
cards2ResId = R.drawable.ill_red_panda_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_red_panda_card3_120_106,
|
||||
batchIds = setOf("AF34"),
|
||||
),
|
||||
|
||||
SatoshiFriends(
|
||||
cards2ResId = R.drawable.ill_satoshi_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_satoshi_card3_120_106,
|
||||
batchIds = setOf("AF19"),
|
||||
),
|
||||
|
||||
Trillant(
|
||||
cards2ResId = R.drawable.ill_trillant_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_trillant_card3_120_106,
|
||||
batchIds = setOf("AF16"),
|
||||
),
|
||||
|
||||
Tron(
|
||||
cards2ResId = R.drawable.ill_tron_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_tron_card3_120_106,
|
||||
batchIds = setOf("AF07"),
|
||||
),
|
||||
|
||||
VeChain(
|
||||
cards2ResId = R.drawable.ill_vechain_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_vechain_card3_120_106,
|
||||
batchIds = setOf("AF29"),
|
||||
),
|
||||
|
||||
// for multicolored cards use image of 3 cards in all cases
|
||||
Vivid(
|
||||
cards2ResId = R.drawable.ill_vivid_cards3_120_106,
|
||||
cards3ResId = R.drawable.ill_vivid_cards3_120_106,
|
||||
batchIds = setOf("AF40", "AF41", "AF42"),
|
||||
),
|
||||
|
||||
VoltInu(
|
||||
cards2ResId = R.drawable.ill_volt_inu_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_volt_inu_card3_120_106,
|
||||
batchIds = setOf("AF35"),
|
||||
),
|
||||
|
||||
WhiteTangem(
|
||||
cards2ResId = R.drawable.ill_white_card2_120_106,
|
||||
cards3ResId = R.drawable.ill_white_card3_120_106,
|
||||
batchIds = setOf("AF15"),
|
||||
),
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.getCardsCount
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.getCardsCount
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
|
@ -12,7 +12,6 @@ import com.tangem.feature.wallet.impl.R
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO: make flexible to integrate cobrands ([REDACTED_JIRA])
|
||||
internal object WalletImageResolver {
|
||||
|
||||
private const val WALLET_WITHOUT_BACKUP_COUNT = 1
|
||||
|
|
@ -24,42 +23,42 @@ internal object WalletImageResolver {
|
|||
@DrawableRes
|
||||
fun resolve(userWallet: UserWallet): Int? {
|
||||
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
|
||||
|
||||
val cobrandImage = Wallet2CobrandImage.entries.firstOrNull {
|
||||
it.batchIds.contains(userWallet.scanResponse.card.batchId)
|
||||
}
|
||||
|
||||
val noteImage by lazy {
|
||||
val noteBlockchain = cardTypesResolver.getBlockchain()
|
||||
|
||||
NoteImage.entries.firstOrNull { it.blockchain == noteBlockchain }
|
||||
}
|
||||
|
||||
return when {
|
||||
cardTypesResolver.isDevKit() -> R.drawable.ill_dev_120_106
|
||||
cardTypesResolver.isWhiteWallet2() -> userWallet.resolveWhiteWallet2()
|
||||
cardTypesResolver.isAvroraWallet() -> userWallet.resolveAvroraWallet()
|
||||
cardTypesResolver.isTraillantWallet() -> userWallet.resolveTraillantWallet()
|
||||
cardTypesResolver.isTronWallet() -> userWallet.resolveTronWallet()
|
||||
cardTypesResolver.isKaspaWallet() -> userWallet.resolveKaspaWallet()
|
||||
cardTypesResolver.isKaspa2Wallet() -> userWallet.resolveKaspa2Wallet()
|
||||
cardTypesResolver.isKaspaResellerWallet() -> userWallet.resolveKaspaResellerWallet()
|
||||
cardTypesResolver.isBadWallet() -> userWallet.resolveBadWallet()
|
||||
cardTypesResolver.isJrWallet() -> userWallet.resolveJrWallet()
|
||||
cardTypesResolver.isGrimWallet() -> userWallet.resolveGrimWallet()
|
||||
cardTypesResolver.isSatoshiFriendsWallet() -> userWallet.resolveSatoshiWallet()
|
||||
cardTypesResolver.isBitcoinPizzaDayWallet() -> userWallet.resolveBitcoinPizzaDayWallet()
|
||||
cardTypesResolver.isVeChainWallet() -> userWallet.resolveVeChainWallet()
|
||||
cardTypesResolver.isNewWorldEliteWallet() -> userWallet.resolveNewWorldEliteWallet()
|
||||
cardTypesResolver.isRedPandaWallet() -> userWallet.resolveRedPandaWallet()
|
||||
cardTypesResolver.isCryptoSethWallet() -> userWallet.resolveCryptoSethWallet()
|
||||
cardTypesResolver.isKishuInuWallet() -> userWallet.resolveKishuInuWallet()
|
||||
cardTypesResolver.isBabyDogeWallet() -> userWallet.resolveBabyDogeWallet()
|
||||
cardTypesResolver.isCOQWallet() -> userWallet.resolveCOQWallet()
|
||||
cardTypesResolver.isCoinMetricaWallet() -> userWallet.resolveCoinMetricaWallet()
|
||||
cardTypesResolver.isVoltInuWallet() -> userWallet.resolveVoltInuWallet()
|
||||
cardTypesResolver.isVividWallet() -> userWallet.resolveVividWallet()
|
||||
cardTypesResolver.isPastelWallet() -> userWallet.resolvePastelWallet()
|
||||
cobrandImage != null -> userWallet.resolveWallet2Cobrand(image = cobrandImage)
|
||||
cardTypesResolver.isWallet2() -> userWallet.resolveWallet2()
|
||||
cardTypesResolver.isShibaWallet() -> userWallet.resolveShibaWallet()
|
||||
cardTypesResolver.isTangemWallet() -> userWallet.resolveWallet1()
|
||||
cardTypesResolver.isWhiteWallet() -> R.drawable.ill_wallet_old_white_120_106
|
||||
cardTypesResolver.isTangemTwins() -> R.drawable.ill_twins_120_106
|
||||
cardTypesResolver.isStart2Coin() -> R.drawable.ill_start2coin_120_106
|
||||
cardTypesResolver.isTangemNote() -> resolveNote(blockchain = cardTypesResolver.getBlockchain())
|
||||
cardTypesResolver.isTangemNote() -> noteImage?.imageResId
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveWallet2Cobrand(image: Wallet2CobrandImage): Int? {
|
||||
return resolveWallet2(oneBackupResId = image.cards2ResId, twoBackupResId = image.cards3ResId)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveShibaWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_shiba_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_shiba_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveWallet2(
|
||||
@DrawableRes oneBackupResId: Int = R.drawable.ill_wallet2_cards2_120_106,
|
||||
@DrawableRes twoBackupResId: Int = R.drawable.ill_wallet2_cards3_120_106,
|
||||
|
|
@ -75,176 +74,6 @@ internal object WalletImageResolver {
|
|||
}
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveTronWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_tron_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_tron_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveKaspaWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_kaspa_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_kaspa_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveKaspa2Wallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_kaspa2_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_kaspa2_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveKaspaResellerWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_kaspa_reseller_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_kaspa_reseller_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveBadWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_bad_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_bad_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveJrWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_jr_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_jr_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveGrimWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_grim_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_grim_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveSatoshiWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_satoshi_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_satoshi_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveShibaWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_shiba_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_shiba_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveWhiteWallet2(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_white_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_white_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveAvroraWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_avrora_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_avrora_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveTraillantWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_traillant_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_traillant_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveBitcoinPizzaDayWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_pizza_day_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_pizza_day_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveVeChainWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_vechain_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_vechain_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveNewWorldEliteWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_nwe_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_nwe_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveRedPandaWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_red_panda_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_red_panda_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveCryptoSethWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_crypto_seth_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_crypto_seth_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveKishuInuWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_kishu_inu_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_kishu_inu_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveBabyDogeWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_baby_doge_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_baby_doge_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveCOQWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_coq_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_coq_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveCoinMetricaWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_coin_metrica_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_coin_metrica_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveVoltInuWallet(): Int? {
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_volt_inu_card2_120_106,
|
||||
twoBackupResId = R.drawable.ill_volt_inu_card3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveVividWallet(): Int? {
|
||||
// for multicolored cards use image of 3 cards in all cases
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_vivid_cards3_120_106,
|
||||
twoBackupResId = R.drawable.ill_vivid_cards3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolvePastelWallet(): Int? {
|
||||
// for multicolored cards use image of 3 cards in all cases
|
||||
return resolveWallet2(
|
||||
oneBackupResId = R.drawable.ill_pastel_cards3_120_106,
|
||||
twoBackupResId = R.drawable.ill_pastel_cards3_120_106,
|
||||
)
|
||||
}
|
||||
|
||||
private fun UserWallet.resolveWallet1(): Int? {
|
||||
return resolveWalletWithBackups { count ->
|
||||
when (count) {
|
||||
|
|
@ -261,16 +90,4 @@ internal object WalletImageResolver {
|
|||
|
||||
return if (count != null) resolve(count) else null
|
||||
}
|
||||
|
||||
private fun resolveNote(blockchain: Blockchain): Int? {
|
||||
return when (blockchain) {
|
||||
Blockchain.Bitcoin -> R.drawable.ill_note_btc_120_106
|
||||
Blockchain.Ethereum -> R.drawable.ill_note_ethereum_120_106
|
||||
Blockchain.BSC -> R.drawable.ill_note_binance_120_106
|
||||
Blockchain.Dogecoin -> R.drawable.ill_note_doge_120_106
|
||||
Blockchain.Cardano -> R.drawable.ill_note_cardano_120_106
|
||||
Blockchain.XRP -> R.drawable.ill_note_xrp_120_106
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import arrow.core.Either
|
|||
import arrow.core.getOrElse
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.common.util.getCardsCount
|
||||
import com.tangem.domain.visa.model.VisaCurrency
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBlockState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.util.getCardsCount
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.common.util.getCardsCount
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import timber.log.Timber
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert
|
|||
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.util.getCardsCount
|
||||
import com.tangem.domain.tokens.model.TotalFiatBalance
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
is TokenActionsState.ActionState.Stake -> {
|
||||
title = resourceReference(R.string.common_stake)
|
||||
icon = R.drawable.ic_staking_24
|
||||
action = { clickIntents.onStakeClick(cryptoCurrencyStatus) }
|
||||
action = { clickIntents.onStakeClick(cryptoCurrencyStatus, actionsState.yield) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Sell -> {
|
||||
title = resourceReference(R.string.common_sell)
|
||||
|
|
@ -77,7 +77,13 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
is TokenActionsState.ActionState.Swap -> {
|
||||
title = resourceReference(R.string.swapping_swap_action)
|
||||
icon = R.drawable.ic_exchange_horizontal_24
|
||||
action = { clickIntents.onSwapClick(cryptoCurrencyStatus, ScenarioUnavailabilityReason.None) }
|
||||
action = {
|
||||
clickIntents.onSwapClick(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
userWalletId = userWallet.walletId,
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.None,
|
||||
)
|
||||
}
|
||||
}
|
||||
is TokenActionsState.ActionState.CopyAddress -> {
|
||||
title = resourceReference(R.string.common_copy_address)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert
|
|||
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.util.getCardsCount
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
|
|
|
|||
|
|
@ -1,130 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TokenItemStateConverter(
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : Converter<CryptoCurrencyStatus, TokenItemState> {
|
||||
|
||||
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
|
||||
|
||||
override fun convert(value: CryptoCurrencyStatus): TokenItemState {
|
||||
return when (value.value) {
|
||||
is CryptoCurrencyStatus.Loading -> value.mapToLoadingState()
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.NoQuote,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
-> value.mapToTokenItemState()
|
||||
is CryptoCurrencyStatus.MissedDerivation -> value.mapToNoAddressTokenItemState()
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> value.mapToUnreachableTokenItemState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToLoadingState(): TokenItemState.Loading {
|
||||
return TokenItemState.Loading(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(value = this),
|
||||
titleState = TokenItemState.TitleState.Content(text = currency.name),
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToTokenItemState(): TokenItemState.Content {
|
||||
return TokenItemState.Content(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(value = this),
|
||||
titleState = TokenItemState.TitleState.Content(
|
||||
text = currency.name,
|
||||
hasPending = value.hasCurrentNetworkTransactions,
|
||||
),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(
|
||||
text = getFormattedFiatAmount(),
|
||||
hasStaked = !getStakedBalance().isZero(),
|
||||
),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = getFormattedAmount()),
|
||||
cryptoPriceState = getCryptoPriceState(),
|
||||
onItemClick = { clickIntents.onTokenItemClick(this) },
|
||||
onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
|
||||
val amount = value.amount?.plus(getStakedBalance()) ?: return DASH_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String {
|
||||
val fiatYieldBalance = value.fiatRate?.times(getStakedBalance()).orZero()
|
||||
val fiatAmount = value.fiatAmount?.plus(fiatYieldBalance) ?: return DASH_SIGN
|
||||
val appCurrency = appCurrencyProvider()
|
||||
|
||||
return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getStakedBalance() =
|
||||
(value.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero()
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToUnreachableTokenItemState() = TokenItemState.Unreachable(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(value = this),
|
||||
titleState = TokenItemState.TitleState.Content(text = currency.name),
|
||||
onItemClick = { clickIntents.onTokenItemClick(this) },
|
||||
onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) },
|
||||
)
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToNoAddressTokenItemState() = TokenItemState.NoAddress(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(this),
|
||||
titleState = TokenItemState.TitleState.Content(text = currency.name),
|
||||
onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) },
|
||||
)
|
||||
|
||||
private fun CryptoCurrencyStatus.getCryptoPriceState(): TokenItemState.CryptoPriceState {
|
||||
val fiatRate = value.fiatRate
|
||||
val priceChange = value.priceChange
|
||||
|
||||
return if (fiatRate != null && priceChange != null) {
|
||||
TokenItemState.CryptoPriceState.Content(
|
||||
price = fiatRate.getFormattedCryptoPrice(),
|
||||
priceChangePercent = BigDecimalFormatter.formatPercent(
|
||||
percent = priceChange,
|
||||
useAbsoluteValue = true,
|
||||
),
|
||||
type = priceChange.getPriceChangeType(),
|
||||
)
|
||||
} else {
|
||||
TokenItemState.CryptoPriceState.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
private fun BigDecimal.getFormattedCryptoPrice(): String {
|
||||
val appCurrency = appCurrencyProvider()
|
||||
return BigDecimalFormatter.formatFiatAmountUncapped(
|
||||
fiatAmount = this,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
}
|
||||
|
||||
private fun BigDecimal.getPriceChangeType(): PriceChangeType {
|
||||
return PriceChangeConverter.fromBigDecimal(value = this)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
|
|
@ -11,7 +12,6 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.TokensListItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.mutate
|
||||
|
|
@ -19,15 +19,16 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.OrganizeTokensButtonConfig as WalletOrganizeTokensButtonConfig
|
||||
|
||||
internal class TokenListStateConverter(
|
||||
appCurrency: AppCurrency,
|
||||
private val tokenList: TokenList,
|
||||
private val selectedWallet: UserWallet,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : Converter<WalletTokensListState, WalletTokensListState> {
|
||||
|
||||
private val tokenStatusConverter = TokenItemStateConverter(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
clickIntents = clickIntents,
|
||||
appCurrency = appCurrency,
|
||||
onItemClick = clickIntents::onTokenItemClick,
|
||||
onItemLongClick = clickIntents::onTokenItemLongClick,
|
||||
)
|
||||
|
||||
override fun convert(value: WalletTokensListState): WalletTokensListState {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ package com.tangem.feature.wallet.presentation.wallet.ui
|
|||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.tangem.core.ui.components.AdditionalTextInputDialogParams
|
||||
import com.tangem.core.ui.components.AdditionalTextInputDialogUM
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButton
|
||||
import com.tangem.core.ui.components.DialogButtonUM
|
||||
import com.tangem.core.ui.components.TextInputDialog
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
|
@ -21,12 +21,12 @@ internal fun WalletAlert(state: WalletAlertState, onDismiss: () -> Unit) {
|
|||
|
||||
@Composable
|
||||
private fun BasicAlert(state: WalletAlertState.Basic, onDismiss: () -> Unit) {
|
||||
val confirmButton: DialogButton
|
||||
val dismissButton: DialogButton?
|
||||
val confirmButton: DialogButtonUM
|
||||
val dismissButton: DialogButtonUM?
|
||||
|
||||
val onActionClick = state.onConfirmClick
|
||||
if (onActionClick != null) {
|
||||
confirmButton = DialogButton(
|
||||
confirmButton = DialogButtonUM(
|
||||
title = state.confirmButtonText.resolveReference(),
|
||||
warning = state.isWarningConfirmButton,
|
||||
onClick = {
|
||||
|
|
@ -34,12 +34,12 @@ private fun BasicAlert(state: WalletAlertState.Basic, onDismiss: () -> Unit) {
|
|||
onDismiss()
|
||||
},
|
||||
)
|
||||
dismissButton = DialogButton(
|
||||
dismissButton = DialogButtonUM(
|
||||
title = stringResource(id = R.string.common_cancel),
|
||||
onClick = onDismiss,
|
||||
)
|
||||
} else {
|
||||
confirmButton = DialogButton(
|
||||
confirmButton = DialogButtonUM(
|
||||
title = state.confirmButtonText.resolveReference(),
|
||||
warning = state.isWarningConfirmButton,
|
||||
onClick = onDismiss,
|
||||
|
|
@ -62,7 +62,7 @@ private fun TextInputAlert(state: WalletAlertState.TextInput, onDismiss: () -> U
|
|||
|
||||
TextInputDialog(
|
||||
fieldValue = value,
|
||||
confirmButton = DialogButton(
|
||||
confirmButton = DialogButtonUM(
|
||||
title = state.confirmButtonText.resolveReference(),
|
||||
enabled = value.text.isNotEmpty() &&
|
||||
value.text != state.text &&
|
||||
|
|
@ -75,8 +75,8 @@ private fun TextInputAlert(state: WalletAlertState.TextInput, onDismiss: () -> U
|
|||
onDismissDialog = onDismiss,
|
||||
onValueChange = { value = it },
|
||||
title = state.title.resolveReference(),
|
||||
dismissButton = DialogButton(title = stringResource(id = R.string.common_cancel), onClick = onDismiss),
|
||||
textFieldParams = AdditionalTextInputDialogParams(
|
||||
dismissButton = DialogButtonUM(title = stringResource(id = R.string.common_cancel), onClick = onDismiss),
|
||||
textFieldParams = AdditionalTextInputDialogUM(
|
||||
label = state.label.resolveReference(),
|
||||
isError = state.errorTextProvider(value.text) != null,
|
||||
caption = state.errorTextProvider(value.text)?.resolveReference(),
|
||||
|
|
|
|||
|
|
@ -74,10 +74,9 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.VisaTxDe
|
|||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balancesAndLimitsBlock
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.depositButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
|
||||
import com.tangem.features.markets.component.BottomSheetState
|
||||
import com.tangem.features.markets.component.BottomSheetState.COLLAPSED
|
||||
import com.tangem.features.markets.component.BottomSheetState.EXPANDED
|
||||
import com.tangem.features.markets.component.MarketsEntryComponent
|
||||
import com.tangem.features.markets.entry.BottomSheetState
|
||||
import com.tangem.features.markets.entry.BottomSheetState.*
|
||||
import com.tangem.features.markets.entry.MarketsEntryComponent
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.token.TokenItem
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.presentation.common.component.NetworkGroupItem
|
||||
import com.tangem.feature.wallet.presentation.common.component.TokenItem
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.TokensListItemState
|
||||
|
||||
/**
|
||||
|
|
@ -26,7 +28,11 @@ internal fun MultiCurrencyContentItem(
|
|||
NetworkGroupItem(networkName = state.name.resolveReference(), modifier = modifier)
|
||||
}
|
||||
is TokensListItemState.Token -> {
|
||||
TokenItem(state = state.state, isBalanceHidden = isBalanceHidden, modifier = modifier)
|
||||
TokenItem(
|
||||
state = state.state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = modifier.background(color = TangemTheme.colors.background.primary),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.viewmodels
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.common.util.getCardsCount
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED_WALLET_INDEX
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
override fun onManageTokensClick() {
|
||||
analyticsEventHandler.send(PortfolioEvent.ButtonManageTokens)
|
||||
reduxStateHolder.dispatch(action = TokensAction.SetArgs.ManageAccess)
|
||||
router.openManageTokensScreen()
|
||||
router.openManageTokensScreen(userWalletId = stateHolder.getSelectedWalletId())
|
||||
}
|
||||
|
||||
override fun onOrganizeTokensClick() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.tokens.getUnavailabilityReasonText
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
|
|
@ -12,7 +14,6 @@ import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModel
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.WrappedList
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.haptic.TangemHapticEffect
|
||||
import com.tangem.core.ui.haptic.VibratorHapticManager
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
|
|
@ -20,7 +21,7 @@ import com.tangem.domain.appcurrency.extenstions.unwrap
|
|||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.staking.GetYieldUseCase
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -57,11 +58,15 @@ interface WalletCurrencyActionsClickIntents {
|
|||
|
||||
fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
fun onSwapClick(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
userWalletId: UserWalletId,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
)
|
||||
|
||||
fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus, yield: Yield?)
|
||||
|
||||
fun onCopyAddressLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus): TextReference?
|
||||
|
||||
|
|
@ -94,7 +99,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val vibratorHapticManager: VibratorHapticManager,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
private val getYieldUseCase: GetYieldUseCase,
|
||||
private val appRouter: AppRouter,
|
||||
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
|
||||
|
||||
override fun onSendClick(
|
||||
|
|
@ -223,19 +228,16 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
walletManagersFacade.getAddress(
|
||||
walletManagersFacade.getDefaultAddress(
|
||||
userWalletId = stateHolder.getSelectedWalletId(),
|
||||
network = cryptoCurrencyStatus.currency.network,
|
||||
)
|
||||
.find { it.type == AddressType.Default }
|
||||
?.value
|
||||
?.let {
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = stateHolder.getSelectedWalletId()))
|
||||
)?.let {
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = stateHolder.getSelectedWalletId()))
|
||||
|
||||
walletEventSender.send(
|
||||
event = WalletEvent.CopyAddress(address = it),
|
||||
)
|
||||
}
|
||||
walletEventSender.send(
|
||||
event = WalletEvent.CopyAddress(address = it),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -355,6 +357,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
override fun onSwapClick(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
userWalletId: UserWalletId,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
) {
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -363,32 +366,31 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
reduxStateHolder.dispatch(TradeCryptoAction.Swap(cryptoCurrencyStatus.currency))
|
||||
appRouter.push(
|
||||
AppRoute.Swap(
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
userWalletId = userWalletId,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onExploreClick() {
|
||||
showErrorIfDemoModeOrElse(action = ::openExplorer)
|
||||
}
|
||||
|
||||
override fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
override fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus, yield: Yield?) {
|
||||
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWallet.walletId))
|
||||
|
||||
viewModelScope.launch {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val yield = getYieldUseCase.invoke(
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
).getOrElse {
|
||||
error("Staking is unavailable for ${cryptoCurrency.name}")
|
||||
}
|
||||
|
||||
reduxStateHolder.dispatch(
|
||||
TradeCryptoAction.Stake(
|
||||
appRouter.push(
|
||||
AppRoute.Staking(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
yield = yield,
|
||||
yield = yield ?: return@launch,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -477,14 +479,12 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private fun handleUnavailabilityReason(unavailabilityReason: ScenarioUnavailabilityReason): Boolean {
|
||||
if (unavailabilityReason == ScenarioUnavailabilityReason.None) return false
|
||||
|
||||
val unavailabilityReasonText = getUnavailabilityReasonText(unavailabilityReason)
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
walletEventSender.send(
|
||||
event = WalletEvent.ShowAlert(
|
||||
state = WalletAlertState.DefaultAlert(
|
||||
title = null,
|
||||
message = unavailabilityReasonText,
|
||||
message = unavailabilityReason.getUnavailabilityReasonText(),
|
||||
onConfirmClick = null,
|
||||
),
|
||||
),
|
||||
|
|
@ -493,66 +493,4 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getUnavailabilityReasonText(unavailabilityReason: ScenarioUnavailabilityReason): TextReference {
|
||||
return when (unavailabilityReason) {
|
||||
is ScenarioUnavailabilityReason.StakingUnavailable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_staking_unavailable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.PendingTransaction -> {
|
||||
when (unavailabilityReason.withdrawalScenario) {
|
||||
ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_pending_transaction_send,
|
||||
formatArgs = wrappedList(unavailabilityReason.networkName),
|
||||
)
|
||||
ScenarioUnavailabilityReason.WithdrawalScenario.SELL -> resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_pending_transaction_sell,
|
||||
formatArgs = wrappedList(unavailabilityReason.networkName),
|
||||
)
|
||||
}
|
||||
}
|
||||
is ScenarioUnavailabilityReason.EmptyBalance -> {
|
||||
when (unavailabilityReason.withdrawalScenario) {
|
||||
ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_empty_balance_send,
|
||||
)
|
||||
ScenarioUnavailabilityReason.WithdrawalScenario.SELL -> resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_empty_balance_sell,
|
||||
)
|
||||
}
|
||||
}
|
||||
is ScenarioUnavailabilityReason.BuyUnavailable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_buy_unavailable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.NotExchangeable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_not_exchangeable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.NotSupportedBySellService -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_sell_unavailable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
ScenarioUnavailabilityReason.Unreachable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_generic_description,
|
||||
)
|
||||
}
|
||||
ScenarioUnavailabilityReason.UnassociatedAsset -> resourceReference(
|
||||
id = R.string.warning_receive_blocked_hedera_token_association_required_message,
|
||||
)
|
||||
ScenarioUnavailabilityReason.None -> {
|
||||
throw IllegalArgumentException("The unavailability reason must be other than None")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class NoteImageTest {
|
||||
|
||||
@Test
|
||||
fun check() {
|
||||
// check uniqueness
|
||||
Truth.assertThat(NoteImage.entries.map { it.blockchain }).containsNoDuplicates()
|
||||
Truth.assertThat(NoteImage.entries.map { it.imageResId }).containsNoDuplicates()
|
||||
|
||||
// check blockchain matching
|
||||
Truth.assertThat(NoteImage.Bitcoin.blockchain).isEqualTo(Blockchain.Bitcoin)
|
||||
Truth.assertThat(NoteImage.Ethereum.blockchain).isEqualTo(Blockchain.Ethereum)
|
||||
Truth.assertThat(NoteImage.Binance.blockchain).isEqualTo(Blockchain.BSC)
|
||||
Truth.assertThat(NoteImage.Dogecoin.blockchain).isEqualTo(Blockchain.Dogecoin)
|
||||
Truth.assertThat(NoteImage.Cardano.blockchain).isEqualTo(Blockchain.Cardano)
|
||||
Truth.assertThat(NoteImage.XRP.blockchain).isEqualTo(Blockchain.XRP)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class Wallet2CobrandImageTest {
|
||||
|
||||
@Test
|
||||
fun checkUniqueness() {
|
||||
val excluded = listOf(Wallet2CobrandImage.Pastel, Wallet2CobrandImage.Vivid)
|
||||
(Wallet2CobrandImage.entries - excluded).forEach {
|
||||
Truth.assertThat(it.cards2ResId != it.cards3ResId).isTrue()
|
||||
}
|
||||
|
||||
Truth.assertThat(Wallet2CobrandImage.entries.map { it.cards2ResId }).containsNoDuplicates()
|
||||
Truth.assertThat(Wallet2CobrandImage.entries.map { it.cards3ResId }).containsNoDuplicates()
|
||||
Truth.assertThat(Wallet2CobrandImage.entries.flatMap { it.batchIds }).containsNoDuplicates()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue