diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index af94ad33ae..245601c136 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -471,6 +471,7 @@
Аккаунт не создан
Эта карта не поддерживается
Ваша карта Tangem предназначена для работы с другим приложением. Пожалуйста, ознакомьтесь с названием и инструкциями на вашей карте и установите правильное приложение.
+ Сеть %s
Адрес скопирован в буфер обмена
Нет соединения с интернетом
Получение
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 038f2a7fe9..3874bb14cb 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -463,6 +463,7 @@
Account is not created
This card is not supported
Your Tangem card was made to work with a different application. Please see the name and instructions on your card, and install the correct app
+ %s network
Address was copied to clipboard
No internet connection
Receiving
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt
index 50651d2e35..8e63ac74d1 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt
@@ -1,29 +1,9 @@
package com.tangem.core.ui.components
import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.IntrinsicSize
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.wrapContentSize
-import androidx.compose.material.Button
-import androidx.compose.material.ButtonColors
-import androidx.compose.material.ButtonDefaults
-import androidx.compose.material.ButtonElevation
-import androidx.compose.material.CircularProgressIndicator
-import androidx.compose.material.Divider
-import androidx.compose.material.Icon
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.*
+import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -244,6 +224,78 @@ fun SelectorButton(text: String, onClick: () -> Unit, modifier: Modifier = Modif
}
// endregion Other
+// region Action
+
+/**
+ * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=290-305&t=3z98eFnTeyIx5TH5-4)
+ * */
+@Composable
+fun RoundedActionButton(
+ text: String,
+ icon: Painter,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+) {
+ TangemButton(
+ modifier = modifier,
+ text = text,
+ icon = TangemButtonIcon.Left(icon),
+ onClick = onClick,
+ enabled = enabled,
+ showProgress = false,
+ colors = TangemButtonsDefaults.secondaryButtonColors,
+ size = TangemButtonSize.RoundedAction,
+ )
+}
+
+/**
+ * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=1208-1395&t=3z98eFnTeyIx5TH5-4)
+ * */
+@Composable
+fun ActionButton(
+ text: String,
+ icon: Painter,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+) {
+ TangemButton(
+ modifier = modifier,
+ text = text,
+ icon = TangemButtonIcon.Left(icon),
+ onClick = onClick,
+ enabled = enabled,
+ showProgress = false,
+ colors = TangemButtonsDefaults.secondaryButtonColors,
+ size = TangemButtonSize.Action,
+ )
+}
+
+/**
+ * Same as [RoundedActionButton] but colored in primary background color
+ * */
+@Composable
+fun BackgroundActionButton(
+ text: String,
+ icon: Painter,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+) {
+ TangemButton(
+ modifier = modifier,
+ text = text,
+ icon = TangemButtonIcon.Left(icon),
+ onClick = onClick,
+ enabled = enabled,
+ showProgress = false,
+ colors = TangemButtonsDefaults.backgroundButtonColors,
+ size = TangemButtonSize.RoundedAction,
+ )
+}
+// endregion Action
+
// region Defaults
@Suppress("LongParameterList")
@Composable
@@ -335,13 +387,12 @@ private fun ButtonContent(
}
}
-sealed interface TangemButtonIcon {
+@Immutable
+private sealed interface TangemButtonIcon {
val painter: Painter?
- @Immutable
data class Left(override val painter: Painter) : TangemButtonIcon
- @Immutable
data class Right(override val painter: Painter) : TangemButtonIcon
object None : TangemButtonIcon {
@@ -349,10 +400,12 @@ sealed interface TangemButtonIcon {
}
}
-enum class TangemButtonSize {
+private enum class TangemButtonSize {
Default,
Text,
Selector,
+ Action,
+ RoundedAction,
}
@Composable
@@ -360,6 +413,9 @@ private fun TangemButtonSize.toHeightDp(): Dp = when (this) {
TangemButtonSize.Default -> TangemTheme.dimens.size48
TangemButtonSize.Text -> TangemTheme.dimens.size40
TangemButtonSize.Selector -> TangemTheme.dimens.size24
+ TangemButtonSize.Action,
+ TangemButtonSize.RoundedAction,
+ -> TangemTheme.dimens.size36
}
@Composable
@@ -367,6 +423,8 @@ private fun TangemButtonSize.toShape(): Shape = when (this) {
TangemButtonSize.Default -> TangemTheme.shapes.roundedCornersMedium
TangemButtonSize.Text -> TangemTheme.shapes.roundedCornersSmall
TangemButtonSize.Selector -> TangemTheme.shapes.roundedCornersSmall
+ TangemButtonSize.Action -> TangemTheme.shapes.roundedCornersMedium
+ TangemButtonSize.RoundedAction -> TangemTheme.shapes.roundedCornersLarge
}
@Composable
@@ -374,6 +432,9 @@ private fun TangemButtonSize.toIconPadding(): Dp = when (this) {
TangemButtonSize.Default -> TangemTheme.dimens.spacing8
TangemButtonSize.Text -> TangemTheme.dimens.spacing8
TangemButtonSize.Selector -> 0.dp
+ TangemButtonSize.Action,
+ TangemButtonSize.RoundedAction,
+ -> TangemTheme.dimens.spacing8
}
object TangemButtonsDefaults {
@@ -423,6 +484,14 @@ object TangemButtonsDefaults {
disabledBackgroundColor = Color.Transparent,
disabledContentColor = TangemTheme.colors.text.disabled,
)
+
+ val backgroundButtonColors: ButtonColors
+ @Composable get() = TangemButtonColors(
+ backgroundColor = TangemTheme.colors.background.primary,
+ contentColor = TangemTheme.colors.text.primary1,
+ disabledBackgroundColor = TangemTheme.colors.button.disabled,
+ disabledContentColor = TangemTheme.colors.text.disabled,
+ )
}
@Immutable
@@ -650,4 +719,67 @@ private fun TextButtonPreview_Dark() {
TextButtonSample()
}
}
+
+@Composable
+private fun ActionButtonSample(modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier
+ .background(TangemTheme.colors.background.primary),
+ ) {
+ RoundedActionButton(
+ text = "Send",
+ icon = painterResource(id = R.drawable.ic_arrow_up_24),
+ onClick = { /* [REDACTED_TODO_COMMENT]*/ },
+ )
+ Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
+ ActionButton(
+ text = "Send",
+ icon = painterResource(id = R.drawable.ic_arrow_up_24),
+ onClick = { /* [REDACTED_TODO_COMMENT]*/ },
+ )
+ Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
+ BackgroundActionButton(
+ text = "Send",
+ icon = painterResource(id = R.drawable.ic_arrow_up_24),
+ onClick = { /* [REDACTED_TODO_COMMENT]*/ },
+ )
+ Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
+ RoundedActionButton(
+ text = "Send",
+ icon = painterResource(id = R.drawable.ic_arrow_up_24),
+ enabled = false,
+ onClick = { /* [REDACTED_TODO_COMMENT]*/ },
+ )
+ Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
+ ActionButton(
+ text = "Send",
+ icon = painterResource(id = R.drawable.ic_arrow_up_24),
+ enabled = false,
+ onClick = { /* [REDACTED_TODO_COMMENT]*/ },
+ )
+ Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8))
+ BackgroundActionButton(
+ text = "Send",
+ icon = painterResource(id = R.drawable.ic_arrow_up_24),
+ enabled = false,
+ onClick = { /* [REDACTED_TODO_COMMENT]*/ },
+ )
+ }
+}
+
+@Preview(showBackground = true, widthDp = 360)
+@Composable
+private fun ActionButtonPreview_Light() {
+ TangemTheme {
+ ActionButtonSample()
+ }
+}
+
+@Preview(showBackground = true, widthDp = 360)
+@Composable
+private fun ActionButtonPreview_Dark() {
+ TangemTheme(isDark = true) {
+ ActionButtonSample()
+ }
+}
// endregion Preview
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt
index 3b6227abbc..e793e21eaf 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt
@@ -14,7 +14,7 @@ import com.valentinilk.shimmer.shimmer
* Rectangle shimmer item with rounded shape from DS
*/
@Composable
-fun ShimmerRectangle(modifier: Modifier = Modifier) {
+fun RectangleShimmer(modifier: Modifier = Modifier) {
Box(
modifier = modifier
.shimmer()
@@ -51,7 +51,7 @@ private fun ShimmersPreview() {
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18),
) {
- ShimmerRectangle(
+ RectangleShimmer(
modifier = Modifier.size(
width = TangemTheme.dimens.size72,
height = TangemTheme.dimens.size12,
diff --git a/core/ui/src/main/res/drawable/ic_group_drop_24.xml b/core/ui/src/main/res/drawable/ic_group_drop_24.xml
new file mode 100644
index 0000000000..6e8c84c533
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_group_drop_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts
index c0a25c65df..183e43c174 100644
--- a/features/wallet/impl/build.gradle.kts
+++ b/features/wallet/impl/build.gradle.kts
@@ -22,6 +22,7 @@ dependencies {
implementation(deps.compose.ui.tooling)
implementation(deps.compose.shimmer)
implementation(deps.compose.accompanist.systemUiController)
+ implementation(deps.kotlin.immutable.collections)
/** DI */
implementation(deps.hilt.android)
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt
new file mode 100644
index 0000000000..619f57d8bb
--- /dev/null
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt
@@ -0,0 +1,161 @@
+package com.tangem.feature.wallet.presentation.common
+
+import com.tangem.core.ui.R
+import com.tangem.feature.wallet.presentation.common.state.NetworkGroupState
+import com.tangem.feature.wallet.presentation.common.state.TokenItemState
+import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState
+import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState
+import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
+import kotlinx.collections.immutable.persistentListOf
+import java.util.UUID
+
+internal object WalletPreviewData {
+
+ val walletCardContent = WalletCardState.Content(
+ id = UUID.randomUUID().toString(),
+ title = "Wallet 1",
+ balance = "8923,05 $",
+ additionalInfo = "3 cards • Seed enabled",
+ imageResId = R.drawable.ill_businessman_3d,
+ onClick = {},
+ )
+
+ val walletCardLoading = WalletCardState.Loading(
+ id = UUID.randomUUID().toString(),
+ title = "Wallet 1",
+ additionalInfo = "3 cards • Seed enabled",
+ imageResId = R.drawable.ill_businessman_3d,
+ onClick = {},
+ )
+
+ val walletCardHiddenContent = WalletCardState.HiddenContent(
+ id = UUID.randomUUID().toString(),
+ title = "Wallet 1",
+ additionalInfo = "3 cards • Seed enabled",
+ imageResId = R.drawable.ill_businessman_3d,
+ onClick = {},
+ )
+
+ val walletCardError = WalletCardState.Error(
+ id = UUID.randomUUID().toString(),
+ title = "Wallet 1",
+ additionalInfo = "3 cards • Seed enabled",
+ imageResId = R.drawable.ill_businessman_3d,
+ onClick = {},
+ )
+
+ val walletScreenState = WalletStateHolder(
+ onBackClick = {},
+ headerConfig = WalletStateHolder.HeaderConfig(
+ wallets = persistentListOf(walletCardContent, walletCardLoading, walletCardHiddenContent, walletCardError),
+ onScanCardClick = {},
+ onMoreClick = {},
+ ),
+ )
+
+ val tokenItemVisibleState = TokenItemState.Content(
+ id = UUID.randomUUID().toString(),
+ tokenIconUrl = null,
+ tokenIconResId = R.drawable.img_polygon_22,
+ networkIconResId = R.drawable.img_polygon_22,
+ name = "Polygon",
+ amount = "5,412 MATIC",
+ hasPending = true,
+ tokenOptions = TokenOptionsState.Visible(
+ fiatAmount = "321 $",
+ priceChange = TokenOptionsState.PriceChange(
+ valuePercent = "2%",
+ type = TokenOptionsState.PriceChange.Type.UP,
+ ),
+ ),
+ )
+
+ val tokenItemHiddenState = TokenItemState.Content(
+ id = UUID.randomUUID().toString(),
+ tokenIconUrl = null,
+ tokenIconResId = R.drawable.img_polygon_22,
+ networkIconResId = R.drawable.img_polygon_22,
+ name = "Polygon",
+ amount = "5,412 MATIC",
+ hasPending = true,
+ tokenOptions = TokenOptionsState.Hidden(
+ priceChange = TokenOptionsState.PriceChange(
+ valuePercent = "2%",
+ type = TokenOptionsState.PriceChange.Type.UP,
+ ),
+ ),
+ )
+
+ val tokenItemDragState = TokenItemState.Draggable(
+ id = UUID.randomUUID().toString(),
+ tokenIconUrl = null,
+ tokenIconResId = R.drawable.img_polygon_22,
+ networkIconResId = R.drawable.img_polygon_22,
+ name = "Polygon",
+ fiatAmount = "3 172,14 $",
+ )
+
+ val tokenItemUnreachableState = TokenItemState.Unreachable(
+ id = UUID.randomUUID().toString(),
+ tokenIconUrl = null,
+ tokenIconResId = R.drawable.img_polygon_22,
+ networkIconResId = R.drawable.img_polygon_22,
+ name = "Polygon",
+ )
+
+ val loadingTokenItemState = TokenItemState.Loading(id = UUID.randomUUID().toString())
+
+ private val draggableTokenList = persistentListOf(
+ tokenItemDragState.copy(
+ id = "token_1",
+ name = "Ethereum",
+ tokenIconResId = R.drawable.img_eth_22,
+ networkIconResId = null,
+ fiatAmount = "3 172,14 $",
+ ),
+ tokenItemDragState.copy(
+ id = "token_2",
+ networkIconResId = R.drawable.img_eth_22,
+ fiatAmount = "803,65 $",
+ ),
+ tokenItemDragState.copy(
+ id = "token_3",
+ name = "USDT",
+ tokenIconResId = R.drawable.img_arbitrum_22,
+ networkIconResId = R.drawable.img_eth_22,
+ fiatAmount = "88,01 $",
+ ),
+ )
+
+ val networkGroup = NetworkGroupState.Content(
+ id = UUID.randomUUID().toString(),
+ networkName = "Ethereum",
+ tokens = persistentListOf(
+ tokenItemVisibleState.copy(
+ id = "token_1",
+ name = "Ethereum",
+ tokenIconResId = R.drawable.img_eth_22,
+ networkIconResId = null,
+ amount = "1,89340821 ETH",
+ ),
+ tokenItemVisibleState.copy(
+ id = "token_2",
+ networkIconResId = R.drawable.img_eth_22,
+ amount = "733,71097 MATIC",
+ ),
+ tokenItemVisibleState.copy(
+ id = "token_3",
+ name = "USDT",
+ tokenIconResId = R.drawable.img_arbitrum_22,
+ networkIconResId = R.drawable.img_eth_22,
+ amount = "0,25404523 ARB",
+ ),
+ ),
+ )
+
+ val draggableNetworkGroup = NetworkGroupState.Draggable(
+ id = UUID.randomUUID().toString(),
+ networkName = "Ethereum",
+ tokens = draggableTokenList,
+ )
+}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt
new file mode 100644
index 0000000000..ee2d924e85
--- /dev/null
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/NetworkGroupItem.kt
@@ -0,0 +1,79 @@
+package com.tangem.feature.wallet.presentation.common.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.key
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.feature.wallet.impl.R
+import com.tangem.feature.wallet.presentation.common.WalletPreviewData
+import com.tangem.feature.wallet.presentation.common.state.NetworkGroupState
+
+@Composable
+internal fun NetworkGroupItem(state: NetworkGroupState, modifier: Modifier = Modifier) {
+ Column(modifier = modifier) {
+ Row(
+ modifier = Modifier
+ .background(TangemTheme.colors.background.primary)
+ .padding(horizontal = TangemTheme.dimens.spacing12)
+ .fillMaxWidth()
+ .heightIn(min = TangemTheme.dimens.size48),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween,
+ ) {
+ Text(
+ text = stringResource(id = R.string.wallet_network_group_title, state.networkName),
+ style = TangemTheme.typography.subtitle2,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ if (state is NetworkGroupState.Draggable) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_group_drop_24),
+ tint = TangemTheme.colors.icon.informative,
+ contentDescription = null,
+ )
+ }
+ }
+ Column {
+ state.tokens.forEach { token ->
+ key(token.id) {
+ TokenItem(state = token)
+ }
+ }
+ }
+ }
+}
+
+// region Preview
+@Preview(showBackground = true, widthDp = 360)
+@Composable
+private fun NetworkGroupItemPreview_Light(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) {
+ TangemTheme {
+ NetworkGroupItem(group)
+ }
+}
+
+@Preview(showBackground = true, widthDp = 360)
+@Composable
+private fun NetworkGroupItemPreview_Dark(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) {
+ TangemTheme(isDark = true) {
+ NetworkGroupItem(group)
+ }
+}
+
+private class NetworkGroupProvider : CollectionPreviewParameterProvider(
+ collection = listOf(
+ WalletPreviewData.networkGroup,
+ WalletPreviewData.draggableNetworkGroup,
+ ),
+)
+// endregion Preview
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt
new file mode 100644
index 0000000000..78c5b5f322
--- /dev/null
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt
@@ -0,0 +1,412 @@
+package com.tangem.feature.wallet.presentation.common.component
+
+import androidx.annotation.DrawableRes
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.ConstrainedLayoutReference
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.ConstraintLayoutScope
+import androidx.constraintlayout.compose.Dimension
+import coil.compose.SubcomposeAsyncImage
+import coil.request.ImageRequest
+import com.tangem.core.ui.components.*
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemTypography
+import com.tangem.feature.wallet.impl.R
+import com.tangem.feature.wallet.presentation.common.WalletPreviewData
+import com.tangem.feature.wallet.presentation.common.state.TokenItemState
+import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState
+
+private const val DOTS = "•••"
+
+@Composable
+internal fun TokenItem(state: TokenItemState, modifier: Modifier = Modifier) {
+ when (state) {
+ is TokenItemState.Content -> ContentTokenItem(state, modifier)
+ is TokenItemState.Loading -> LoadingTokenItem(modifier)
+ is TokenItemState.Draggable -> DraggableTokenItem(state, modifier)
+ is TokenItemState.Unreachable -> UnreachableTokenItem(state, modifier)
+ }
+}
+
+@Composable
+private fun ContentTokenItem(content: TokenItemState.Content, modifier: Modifier = Modifier) {
+ InternalTokenItem(
+ modifier = modifier,
+ name = content.name,
+ tokenIconUrl = content.tokenIconUrl,
+ tokenIconResId = content.tokenIconResId,
+ networkIconResId = content.networkIconResId,
+ amount = if (content.tokenOptions is TokenOptionsState.Hidden) DOTS else content.amount,
+ hasPending = content.hasPending,
+ options = { ref ->
+ TokenOptionsBlock(
+ modifier = Modifier.constrainAsOptionsItem(scope = this, ref),
+ state = content.tokenOptions,
+ )
+ },
+ )
+}
+
+@Composable
+internal fun DraggableTokenItem(state: TokenItemState.Draggable, modifier: Modifier = Modifier) {
+ InternalTokenItem(
+ modifier = modifier,
+ name = state.name,
+ tokenIconUrl = state.tokenIconUrl,
+ tokenIconResId = state.tokenIconResId,
+ networkIconResId = state.networkIconResId,
+ amount = state.fiatAmount,
+ hasPending = false,
+ options = { ref ->
+ Icon(
+ modifier = Modifier.constrainAsOptionsItem(scope = this, ref),
+ painter = painterResource(id = R.drawable.ic_drag_24),
+ tint = TangemTheme.colors.icon.informative,
+ contentDescription = null,
+ )
+ },
+ )
+}
+
+@Composable
+internal fun UnreachableTokenItem(state: TokenItemState.Unreachable, modifier: Modifier = Modifier) {
+ InternalTokenItem(
+ modifier = modifier,
+ name = state.name,
+ tokenIconUrl = state.tokenIconUrl,
+ tokenIconResId = state.tokenIconResId,
+ networkIconResId = state.networkIconResId,
+ amount = null,
+ hasPending = false,
+ options = { ref ->
+ Text(
+ modifier = Modifier.constrainAsOptionsItem(scope = this, ref),
+ text = "Unreachable", // TODO (conform this text)
+ style = TangemTypography.body2,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ },
+ )
+}
+
+@Composable
+private fun LoadingTokenItem(modifier: Modifier = Modifier) {
+ BaseSurface(modifier) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ horizontal = TangemTheme.dimens.spacing12,
+ vertical = TangemTheme.dimens.spacing4,
+ ),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
+ ) {
+ CircleShimmer(modifier = Modifier.size(size = TangemTheme.dimens.size42))
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween,
+ ) {
+ Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
+ RectangleShimmer(
+ modifier = Modifier.size(
+ width = TangemTheme.dimens.size72,
+ height = TangemTheme.dimens.size12,
+ ),
+ )
+ RectangleShimmer(
+ modifier = Modifier.size(
+ width = TangemTheme.dimens.size50,
+ height = TangemTheme.dimens.size12,
+ ),
+ )
+ }
+ Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
+ RectangleShimmer(
+ modifier = Modifier.size(
+ width = TangemTheme.dimens.size40,
+ height = TangemTheme.dimens.size12,
+ ),
+ )
+ RectangleShimmer(
+ modifier = Modifier.size(
+ width = TangemTheme.dimens.size40,
+ height = TangemTheme.dimens.size12,
+ ),
+ )
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Block for end part of token item
+ * shows status is reachable, is drag, hidden or show balance
+ */
+@Composable
+private fun TokenOptionsBlock(state: TokenOptionsState, modifier: Modifier = Modifier) {
+ when (state) {
+ is TokenOptionsState.Visible -> {
+ TokenFiatPercentageBlock(
+ modifier = modifier,
+ fiatAmount = state.fiatAmount,
+ priceChange = state.priceChange,
+ )
+ }
+ is TokenOptionsState.Hidden -> {
+ TokenFiatPercentageBlock(
+ modifier = modifier,
+ fiatAmount = DOTS,
+ priceChange = state.priceChange,
+ )
+ }
+ }
+}
+
+@Suppress("LongParameterList")
+@Composable
+private fun InternalTokenItem(
+ name: String,
+ tokenIconUrl: String?,
+ @DrawableRes tokenIconResId: Int,
+ @DrawableRes networkIconResId: Int?,
+ amount: String?,
+ hasPending: Boolean,
+ options: @Composable ConstraintLayoutScope.(ref: ConstrainedLayoutReference) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ BaseSurface(modifier) {
+ ConstraintLayout(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ horizontal = TangemTheme.dimens.spacing12,
+ vertical = TangemTheme.dimens.spacing4,
+ ),
+ ) {
+ val (iconItem, tokenNameItem, optionsItem) = createRefs()
+
+ TokenIcon(
+ modifier = Modifier.constrainAs(iconItem) {
+ centerVerticallyTo(parent)
+ start.linkTo(parent.start)
+ },
+ tokenIconUrl = tokenIconUrl,
+ tokenIconResId = tokenIconResId,
+ networkIconRes = networkIconResId,
+ )
+
+ TokenTitleAmountBlock(
+ modifier = Modifier
+ .padding(horizontal = TangemTheme.dimens.spacing12)
+ .constrainAs(tokenNameItem) {
+ centerVerticallyTo(parent)
+ start.linkTo(iconItem.end)
+ end.linkTo(optionsItem.start)
+ width = Dimension.fillToConstraints
+ },
+ title = name,
+ amount = amount,
+ hasPending = hasPending,
+ )
+
+ options(optionsItem)
+ }
+ }
+}
+
+@Stable
+private fun Modifier.constrainAsOptionsItem(scope: ConstraintLayoutScope, ref: ConstrainedLayoutReference): Modifier {
+ return with(scope) {
+ this@constrainAsOptionsItem.constrainAs(ref) {
+ centerVerticallyTo(parent)
+ end.linkTo(parent.end)
+ }
+ }
+}
+
+@Composable
+private fun BaseSurface(
+ modifier: Modifier = Modifier,
+ onClick: (() -> Unit)? = null,
+ content: @Composable () -> Unit,
+) {
+ Surface(
+ modifier = modifier.defaultMinSize(minHeight = TangemTheme.dimens.size68),
+ color = TangemTheme.colors.background.primary,
+ onClick = onClick ?: {},
+ enabled = onClick != null,
+ ) {
+ content()
+ }
+}
+
+@Composable
+private fun TokenTitleAmountBlock(title: String, amount: String?, hasPending: Boolean, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
+ ) {
+ Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
+ Text(
+ text = title,
+ style = TangemTypography.subtitle2,
+ color = TangemTheme.colors.text.primary1,
+ )
+ if (hasPending) {
+ Image(
+ modifier = Modifier.align(Alignment.CenterVertically),
+ painter = painterResource(id = R.drawable.img_loader_15),
+ contentDescription = null,
+ )
+ }
+ }
+ if (!amount.isNullOrBlank()) {
+ Text(
+ text = amount,
+ style = TangemTypography.body2,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ }
+ }
+}
+
+@Composable
+private fun TokenFiatPercentageBlock(
+ fiatAmount: String,
+ priceChange: TokenOptionsState.PriceChange,
+ modifier: Modifier = Modifier,
+) {
+ Column(modifier = modifier.requiredWidth(IntrinsicSize.Max)) {
+ Text(
+ modifier = Modifier.align(Alignment.End),
+ text = fiatAmount,
+ style = TangemTypography.body2,
+ color = TangemTheme.colors.text.primary1,
+ )
+ SpacerH2()
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.End,
+ ) {
+ val iconChangeArrow: Int
+ val changeTextColor: Color
+ when (priceChange.type) {
+ TokenOptionsState.PriceChange.Type.UP -> {
+ iconChangeArrow = R.drawable.img_arrow_up_8
+ changeTextColor = TangemTheme.colors.text.accent
+ }
+ TokenOptionsState.PriceChange.Type.DOWN -> {
+ iconChangeArrow = R.drawable.img_arrow_down_8
+ changeTextColor = TangemTheme.colors.text.warning
+ }
+ }
+ Image(
+ modifier = Modifier.align(Alignment.CenterVertically),
+ painter = painterResource(id = iconChangeArrow),
+ contentDescription = null,
+ )
+ SpacerW4()
+ Text(
+ modifier = Modifier.align(Alignment.CenterVertically),
+ text = priceChange.valuePercent,
+ style = TangemTypography.body2,
+ color = changeTextColor,
+ )
+ }
+ }
+}
+
+@Composable
+private fun TokenIcon(
+ tokenIconUrl: String?,
+ modifier: Modifier = Modifier,
+ @DrawableRes tokenIconResId: Int? = null,
+ @DrawableRes networkIconRes: Int? = null,
+) {
+ Box(
+ modifier = modifier
+ .padding(end = TangemTheme.dimens.spacing16)
+ .size(TangemTheme.dimens.size42),
+ ) {
+ val tokenImageModifier = Modifier
+ .align(Alignment.BottomStart)
+ .size(TangemTheme.dimens.size36)
+
+ val data = if (tokenIconUrl.isNullOrEmpty()) tokenIconResId else tokenIconUrl
+ SubcomposeAsyncImage(
+ modifier = tokenImageModifier,
+ model = ImageRequest.Builder(LocalContext.current)
+ .data(data)
+ .crossfade(true)
+ .build(),
+ loading = { CircleShimmer(modifier = tokenImageModifier) },
+ contentDescription = null,
+ )
+
+ if (networkIconRes != null) {
+ Box(
+ modifier = Modifier
+ .align(Alignment.TopEnd)
+ .size(TangemTheme.dimens.size18)
+ .background(color = Color.White, shape = CircleShape),
+ contentAlignment = Alignment.Center,
+ ) {
+ Image(
+ modifier = Modifier.padding(all = 0.5.dp),
+ painter = painterResource(id = networkIconRes),
+ contentDescription = null,
+ )
+ }
+ }
+ }
+}
+
+// region preview
+
+@Preview
+@Composable
+private fun Preview_Tokens_LightTheme(@PreviewParameter(TokenConfigProvider::class) state: TokenItemState) {
+ TangemTheme(isDark = false) {
+ TokenItem(state)
+ }
+}
+
+@Preview
+@Composable
+private fun Preview_Tokens_DarkTheme(@PreviewParameter(TokenConfigProvider::class) state: TokenItemState) {
+ TangemTheme(isDark = true) {
+ TokenItem(state)
+ }
+}
+
+private class TokenConfigProvider : CollectionPreviewParameterProvider(
+ collection = listOf(
+ WalletPreviewData.tokenItemVisibleState,
+ WalletPreviewData.tokenItemUnreachableState,
+ WalletPreviewData.tokenItemDragState,
+ WalletPreviewData.tokenItemHiddenState,
+ WalletPreviewData.loadingTokenItemState,
+ ),
+)
+
+// endregion preview
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt
new file mode 100644
index 0000000000..706387e9d6
--- /dev/null
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt
@@ -0,0 +1,111 @@
+package com.tangem.feature.wallet.presentation.common.state
+
+import androidx.annotation.DrawableRes
+import androidx.compose.runtime.Immutable
+import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState.PriceChange.Type
+
+/** Token item state */
+@Immutable
+internal sealed interface TokenItemState {
+
+ /** Token id */
+ val id: String
+
+ /**
+ * Loading token state
+ *
+ * @property id token id
+ */
+ data class Loading(override val id: String) : TokenItemState
+
+ /**
+ * Content token state
+ *
+ * @property id token id
+ * @property tokenIconUrl token icon url
+ * @property tokenIconResId token icon resource id
+ * @property networkIconResId network icon resource id, may be null if it is a coin
+ * @property name token name
+ * @property amount amount of token
+ * @property hasPending pending tx in blockchain
+ * @property tokenOptions state for token options
+ */
+ data class Content(
+ override val id: String,
+ val tokenIconUrl: String?,
+ @DrawableRes val tokenIconResId: Int,
+ @DrawableRes val networkIconResId: Int?,
+ val name: String,
+ val amount: String,
+ val hasPending: Boolean,
+ val tokenOptions: TokenOptionsState,
+ ) : TokenItemState
+
+ /**
+ * Draggable token state
+ *
+ * @property id token id
+ * @property tokenIconUrl token icon url
+ * @property tokenIconResId token icon resource id
+ * @property networkIconResId network icon resource id, may be null if it is a coin
+ * @property name token name
+ */
+ data class Draggable(
+ override val id: String,
+ val tokenIconUrl: String?,
+ @DrawableRes val tokenIconResId: Int,
+ @DrawableRes val networkIconResId: Int?,
+ val name: String,
+ val fiatAmount: String,
+ ) : TokenItemState
+
+ /**
+ * Unreachable token state
+ *
+ * @property id token id
+ * @property tokenIconUrl token icon url
+ * @property tokenIconResId token icon resource id
+ * @property networkIconResId network icon resource id, may be null if it is a coin
+ * @property name token name
+ */
+ data class Unreachable(
+ override val id: String,
+ val tokenIconUrl: String?,
+ @DrawableRes val tokenIconResId: Int,
+ @DrawableRes val networkIconResId: Int?,
+ val name: String,
+ ) : TokenItemState
+
+ /** Token options state */
+ sealed interface TokenOptionsState {
+
+ /**
+ * Visible token options state
+ *
+ * @property fiatAmount fiat amount of token
+ * @property priceChange value of price changing
+ */
+ data class Visible(val fiatAmount: String, val priceChange: PriceChange) : TokenOptionsState
+
+ /**
+ * Hidden token options state
+ *
+ * @property priceChange value of price changing
+ */
+ data class Hidden(val priceChange: PriceChange) : TokenOptionsState
+
+ /**
+ * Price changing option state
+ *
+ * @property valuePercent value in percent
+ * @property type type [Type]
+ */
+ data class PriceChange(val valuePercent: String, val type: Type) {
+
+ /** Price changing type */
+ enum class Type {
+ UP, DOWN
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt
new file mode 100644
index 0000000000..c504d1c81e
--- /dev/null
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenListState.kt
@@ -0,0 +1,35 @@
+package com.tangem.feature.wallet.presentation.common.state
+
+import androidx.compose.runtime.Immutable
+import kotlinx.collections.immutable.ImmutableList
+
+@Immutable
+internal sealed interface TokenListState {
+
+ data class GroupedByNetwork(
+ val groups: ImmutableList,
+ ) : TokenListState
+
+ data class Ungrouped(
+ val tokens: ImmutableList,
+ ) : TokenListState
+}
+
+@Immutable
+internal sealed interface NetworkGroupState {
+ val id: String
+ val networkName: String
+ val tokens: ImmutableList
+
+ data class Draggable(
+ override val id: String,
+ override val networkName: String,
+ override val tokens: ImmutableList,
+ ) : NetworkGroupState
+
+ data class Content(
+ override val id: String,
+ override val networkName: String,
+ override val tokens: ImmutableList,
+ ) : NetworkGroupState
+}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt
index 90a84bef1c..74751d0c6b 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt
@@ -10,8 +10,8 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.WalletFragment
-import com.tangem.feature.wallet.presentation.ui.WalletScreen
-import com.tangem.feature.wallet.presentation.viewmodels.WalletViewModel
+import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
+import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel
import kotlin.properties.Delegates
/** Default implementation of wallet feature router */
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt
index 00782aa81d..7558ac61c7 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt
@@ -1,13 +1,18 @@
package com.tangem.feature.wallet.presentation.router
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
import com.tangem.features.wallet.navigation.WalletRouter
/**
* Interface of inner wallet feature router
*
+ * Annotated as [Stable] because implementation of this interface passed as argument to [Initialize] method by compose
+ * compiler
+ *
[REDACTED_AUTHOR]
*/
+@Stable
internal interface InnerWalletRouter : WalletRouter {
/** Initialize router */
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenUIState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenUIState.kt
deleted file mode 100644
index ace321b30a..0000000000
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenUIState.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.tangem.feature.wallet.presentation.state
-
-internal enum class TokenUIState {
- LOADED, LOADING
-}
-
-internal enum class TokenOptionsUIState {
- VISIBLE, HIDDEN, DRAG, UNREACHABLE
-}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenV2Config.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenV2Config.kt
deleted file mode 100644
index dca7440c39..0000000000
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/TokenV2Config.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.tangem.feature.wallet.presentation.state
-
-import androidx.annotation.DrawableRes
-
-/**
- * Token config
- *
- * @property name of token/coin
- * @property amount of token
- * @property fiatAmount amount in fiat
- * @property priceChange value of price changing
- * @property iconUrl
- * @property icon token/coin icon
- * @property networkIcon
- * @property tokenUIState token state [TokenUIState] loading etc.
- * @property tokenOptionsUIState state for token options like 'unreachable', 'hidden' etc
- * @property hasPending pending tx in blockchain
- */
-internal data class TokenV2Config(
- val name: String,
- val amount: String,
- val fiatAmount: String,
- val priceChange: PriceChange,
- val iconUrl: String?,
- @DrawableRes val icon: Int?,
- @DrawableRes val networkIcon: Int,
- val tokenUIState: TokenUIState,
- val tokenOptionsUIState: TokenOptionsUIState,
- val hasPending: Boolean,
-)
-
-data class PriceChange(
- val valuePercent: String,
- val type: PriceChangeType,
-)
-
-enum class PriceChangeType {
- UP, DOWN
-}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt
deleted file mode 100644
index 45b3eb843d..0000000000
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt
+++ /dev/null
@@ -1,352 +0,0 @@
-package com.tangem.feature.wallet.presentation.ui
-
-import androidx.annotation.DrawableRes
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material3.Icon
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import androidx.constraintlayout.compose.ConstraintLayout
-import androidx.constraintlayout.compose.Dimension
-import coil.compose.SubcomposeAsyncImage
-import coil.request.ImageRequest
-import com.tangem.core.ui.components.*
-import com.tangem.core.ui.res.TangemTheme
-import com.tangem.core.ui.res.TangemTypography
-import com.tangem.feature.wallet.impl.R
-import com.tangem.feature.wallet.presentation.state.*
-
-private const val DOTS = "•••"
-
-@Composable
-internal fun TokenItemV2(config: TokenV2Config) {
- when (config.tokenUIState) {
- TokenUIState.LOADED -> LoadedTokenState(config)
- TokenUIState.LOADING -> LoadingTokenState()
- }
-}
-
-@Composable
-private fun LoadedTokenState(config: TokenV2Config) {
- BaseSurface {
- ConstraintLayout(
- modifier = Modifier
- .fillMaxWidth()
- .padding(
- horizontal = TangemTheme.dimens.spacing12,
- vertical = TangemTheme.dimens.spacing4,
- ),
- ) {
- val (iconItem, tokenNameItem, fiatItem) = createRefs()
- TokenIcon(
- modifier = Modifier.constrainAs(iconItem) {
- centerVerticallyTo(parent)
- start.linkTo(parent.start)
- },
- tokenIconUrl = config.iconUrl,
- icon = config.icon,
- networkIconRes = config.networkIcon,
- )
- TokenTitleAmountBlock(
- modifier = Modifier
- .padding(horizontal = TangemTheme.dimens.spacing12)
- .constrainAs(tokenNameItem) {
- centerVerticallyTo(parent)
- start.linkTo(iconItem.end)
- end.linkTo(fiatItem.start)
- width = Dimension.fillToConstraints
- },
- title = config.name,
- amount = config.amount,
- hasPending = config.hasPending,
- )
- TokenOptionsBlock(
- config = config,
- modifier = Modifier.constrainAs(fiatItem) {
- centerVerticallyTo(parent)
- end.linkTo(parent.end)
- },
- )
- }
- }
-}
-
-/**
- * Block for end part of token item
- * shows status is reachable, is drag, hidden or show balance
- */
-@Composable
-private fun TokenOptionsBlock(config: TokenV2Config, modifier: Modifier = Modifier) {
- when (config.tokenOptionsUIState) {
- TokenOptionsUIState.VISIBLE -> TokenFiatPercentageBlock(
- modifier = modifier,
- fiatAmount = config.fiatAmount,
- priceChange = config.priceChange,
- )
- TokenOptionsUIState.UNREACHABLE -> Text(
- modifier = modifier,
- text = "Unreachable", // TODO (conform this text)
- style = TangemTypography.body2,
- color = TangemTheme.colors.text.tertiary,
- )
- TokenOptionsUIState.HIDDEN -> TokenFiatPercentageBlock(
- modifier = modifier,
- fiatAmount = DOTS,
- priceChange = config.priceChange,
- )
- TokenOptionsUIState.DRAG -> Icon(
- modifier = modifier,
- painter = painterResource(id = R.drawable.ic_drag_24),
- tint = TangemTheme.colors.icon.informative,
- contentDescription = null,
- )
- }
-}
-
-@Composable
-private fun LoadingTokenState() {
- BaseSurface {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(
- horizontal = TangemTheme.dimens.spacing12,
- vertical = TangemTheme.dimens.spacing4,
- ),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween,
- ) {
- CircleShimmer(modifier = Modifier.size(size = TangemTheme.dimens.size42))
- SpacerW12()
- Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
- ShimmerRectangle(
- modifier = Modifier.size(
- width = TangemTheme.dimens.size72,
- height = TangemTheme.dimens.size12,
- ),
- )
- ShimmerRectangle(
- modifier = Modifier.size(
- width = TangemTheme.dimens.size50,
- height = TangemTheme.dimens.size12,
- ),
- )
- }
- }
- Column(
- verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
- ) {
- ShimmerRectangle(
- modifier = Modifier.size(
- width = TangemTheme.dimens.size40,
- height = TangemTheme.dimens.size12,
- ),
- )
- ShimmerRectangle(
- modifier = Modifier.size(
- width = TangemTheme.dimens.size40,
- height = TangemTheme.dimens.size12,
- ),
- )
- }
- }
-}
-
-@Composable
-private fun BaseSurface(onClick: (() -> Unit)? = null, content: @Composable () -> Unit) {
- Surface(
- modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size68),
- color = TangemTheme.colors.background.primary,
- onClick = onClick ?: {},
- enabled = onClick != null,
- ) {
- content()
- }
-}
-
-@Composable
-private fun TokenTitleAmountBlock(title: String, amount: String, hasPending: Boolean, modifier: Modifier = Modifier) {
- Column(
- modifier = modifier,
- verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
- ) {
- Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
- Text(
- text = title,
- style = TangemTypography.subtitle2,
- color = TangemTheme.colors.text.primary1,
- )
- if (hasPending) {
- Image(
- modifier = Modifier.align(Alignment.CenterVertically),
- painter = painterResource(id = R.drawable.img_loader_15),
- contentDescription = null,
- )
- }
- }
- Text(
- text = amount,
- style = TangemTypography.body2,
- color = TangemTheme.colors.text.tertiary,
- )
- }
-}
-
-@Composable
-private fun TokenFiatPercentageBlock(fiatAmount: String, priceChange: PriceChange, modifier: Modifier = Modifier) {
- Column(modifier = modifier.requiredWidth(IntrinsicSize.Max)) {
- Text(
- modifier = Modifier.align(Alignment.End),
- text = fiatAmount,
- style = TangemTypography.body2,
- color = TangemTheme.colors.text.primary1,
- )
- SpacerH2()
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.End,
- ) {
- val iconChangeArrow: Int
- val changeTextColor: Color
- when (priceChange.type) {
- PriceChangeType.UP -> {
- iconChangeArrow = R.drawable.img_arrow_up_8
- changeTextColor = TangemTheme.colors.text.accent
- }
- PriceChangeType.DOWN -> {
- iconChangeArrow = R.drawable.img_arrow_down_8
- changeTextColor = TangemTheme.colors.text.warning
- }
- }
- Image(
- modifier = Modifier.align(Alignment.CenterVertically),
- painter = painterResource(id = iconChangeArrow),
- contentDescription = null,
- )
- SpacerW4()
- Text(
- modifier = Modifier.align(Alignment.CenterVertically),
- text = priceChange.valuePercent,
- style = TangemTypography.body2,
- color = changeTextColor,
- )
- }
- }
-}
-
-@Composable
-private fun TokenIcon(
- tokenIconUrl: String?,
- modifier: Modifier = Modifier,
- @DrawableRes icon: Int? = null,
- @DrawableRes networkIconRes: Int? = null,
-) {
- Box(
- modifier = modifier
- .padding(end = TangemTheme.dimens.spacing16)
- .size(TangemTheme.dimens.size42),
- ) {
- val tokenImageModifier = Modifier
- .align(Alignment.BottomStart)
- .size(TangemTheme.dimens.size36)
-
- val data = if (tokenIconUrl.isNullOrEmpty()) {
- icon
- } else {
- tokenIconUrl
- }
- SubcomposeAsyncImage(
- modifier = tokenImageModifier,
- model = ImageRequest.Builder(LocalContext.current)
- .data(data)
- .crossfade(true)
- .build(),
- loading = { CircleShimmer(modifier = tokenImageModifier) },
- contentDescription = null,
- )
-
- if (networkIconRes != null) {
- Box(
- modifier = Modifier
- .align(Alignment.TopEnd)
- .size(TangemTheme.dimens.size18)
- .background(color = Color.White, shape = CircleShape),
- contentAlignment = Alignment.Center,
- ) {
- Image(
- modifier = Modifier.padding(all = 0.5.dp),
- painter = painterResource(id = networkIconRes),
- contentDescription = null,
- )
- }
- }
- }
-}
-
-// region preview
-
-@Composable
-private fun TokensPreview() {
- Column(
- modifier = Modifier.fillMaxWidth(),
- verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18),
- ) {
- val config = TokenV2Config(
- name = "Polygon",
- amount = "5,412 MATIC",
- fiatAmount = "321 $",
- priceChange = PriceChange(
- valuePercent = "2%",
- type = PriceChangeType.UP,
- ),
- iconUrl = null,
- icon = R.drawable.img_polygon_22,
- networkIcon = R.drawable.img_polygon_22,
- tokenUIState = TokenUIState.LOADED,
- tokenOptionsUIState = TokenOptionsUIState.VISIBLE,
- hasPending = true,
- )
- // Loaded item
- TokenItemV2(config)
- // Unreachable item
- TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.UNREACHABLE))
- // Drag item
- TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.DRAG))
- // Hidden item
- TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.UNREACHABLE))
- // Loading item
- TokenItemV2(
- config.copy(
- tokenUIState = TokenUIState.LOADING,
- ),
- )
- }
-}
-
-@Preview(showBackground = true)
-@Composable
-private fun Preview_Tokens_InLightTheme() {
- TangemTheme(isDark = false) {
- TokensPreview()
- }
-}
-
-@Preview(showBackground = true)
-@Composable
-private fun Preview_Tokens_InDarkTheme() {
- TangemTheme(isDark = true) {
- TokensPreview()
- }
-}
-
-// endregion preview
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt
deleted file mode 100644
index b716022f9b..0000000000
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.tangem.feature.wallet.presentation.ui
-
-import com.tangem.core.ui.R
-import com.tangem.feature.wallet.presentation.state.WalletCardState
-import com.tangem.feature.wallet.presentation.state.WalletStateHolder
-import java.util.UUID
-
-internal object WalletPreviewData {
-
- val walletCardContent = WalletCardState.Content(
- id = UUID.randomUUID().toString(),
- title = "Wallet 1",
- balance = "8923,05 $",
- additionalInfo = "3 cards • Seed enabled",
- imageResId = R.drawable.ill_businessman_3d,
- onClick = {},
- )
-
- val walletCardLoading = WalletCardState.Loading(
- id = UUID.randomUUID().toString(),
- title = "Wallet 1",
- additionalInfo = "3 cards • Seed enabled",
- imageResId = R.drawable.ill_businessman_3d,
- onClick = {},
- )
-
- val walletCardHiddenContent = WalletCardState.HiddenContent(
- id = UUID.randomUUID().toString(),
- title = "Wallet 1",
- additionalInfo = "3 cards • Seed enabled",
- imageResId = R.drawable.ill_businessman_3d,
- onClick = {},
- )
-
- val walletCardError = WalletCardState.Error(
- id = UUID.randomUUID().toString(),
- title = "Wallet 1",
- additionalInfo = "3 cards • Seed enabled",
- imageResId = R.drawable.ill_businessman_3d,
- onClick = {},
- )
-
- val walletScreenState = WalletStateHolder(
- onBackClick = {},
- headerConfig = WalletStateHolder.HeaderConfig(
- wallets = listOf(walletCardContent, walletCardLoading, walletCardHiddenContent, walletCardError),
- onScanCardClick = {},
- onMoreClick = {},
- ),
- )
-}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt
similarity index 97%
rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletCardState.kt
rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt
index 0837a3ef7d..7685a267da 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletCardState.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt
@@ -1,4 +1,4 @@
-package com.tangem.feature.wallet.presentation.state
+package com.tangem.feature.wallet.presentation.wallet.state
import androidx.annotation.DrawableRes
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt
similarity index 79%
rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletStateHolder.kt
rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt
index c69ab425ae..8142667ad3 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/state/WalletStateHolder.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt
@@ -1,4 +1,6 @@
-package com.tangem.feature.wallet.presentation.state
+package com.tangem.feature.wallet.presentation.wallet.state
+
+import kotlinx.collections.immutable.ImmutableList
/**
* Wallet state holder
@@ -21,7 +23,7 @@ internal data class WalletStateHolder(
* @property onMoreClick lambda be invoked when more button is clicked
*/
data class HeaderConfig(
- val wallets: List,
+ val wallets: ImmutableList,
val onScanCardClick: () -> Unit,
val onMoreClick: () -> Unit,
)
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt
similarity index 69%
rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletScreen.kt
rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt
index dc166a77a2..ba17e6e692 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletScreen.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt
@@ -1,10 +1,10 @@
-package com.tangem.feature.wallet.presentation.ui
+package com.tangem.feature.wallet.presentation.wallet.ui
import androidx.activity.compose.BackHandler
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
-import com.tangem.feature.wallet.presentation.state.WalletStateHolder
-import com.tangem.feature.wallet.presentation.ui.components.WalletHeader
+import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
+import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletHeader
/**
* Wallet screen
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt
similarity index 95%
rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletCard.kt
rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt
index 25a427408a..562c954023 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletCard.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt
@@ -1,4 +1,4 @@
-package com.tangem.feature.wallet.presentation.ui.components
+package com.tangem.feature.wallet.presentation.wallet.ui.components
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
@@ -17,12 +17,12 @@ import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import com.tangem.core.ui.components.FontSizeRange
+import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.ResizableText
-import com.tangem.core.ui.components.ShimmerRectangle
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R
-import com.tangem.feature.wallet.presentation.state.WalletCardState
-import com.tangem.feature.wallet.presentation.ui.WalletPreviewData
+import com.tangem.feature.wallet.presentation.common.WalletPreviewData
+import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState
private const val DOTS = "•••"
@@ -119,7 +119,7 @@ private fun Balance(state: WalletCardState) {
)
}
is WalletCardState.Loading -> {
- ShimmerRectangle(
+ RectangleShimmer(
modifier = Modifier.size(
width = TangemTheme.dimens.size102,
height = TangemTheme.dimens.size24,
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletHeader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletHeader.kt
similarity index 91%
rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletHeader.kt
rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletHeader.kt
index acfa7b9a49..cb01b48377 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletHeader.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletHeader.kt
@@ -1,4 +1,4 @@
-package com.tangem.feature.wallet.presentation.ui.components
+package com.tangem.feature.wallet.presentation.wallet.ui.components
import androidx.compose.animation.core.*
import androidx.compose.foundation.ExperimentalFoundationApi
@@ -21,9 +21,10 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R
-import com.tangem.feature.wallet.presentation.state.WalletCardState
-import com.tangem.feature.wallet.presentation.state.WalletStateHolder
-import com.tangem.feature.wallet.presentation.ui.WalletPreviewData
+import com.tangem.feature.wallet.presentation.common.WalletPreviewData
+import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState
+import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
+import kotlinx.collections.immutable.persistentListOf
/**
* Wallet screen header
@@ -109,7 +110,7 @@ private fun Preview_WalletHeader_DarkTheme(
private class WalletHeaderProvider : CollectionPreviewParameterProvider(
collection = listOf(
WalletStateHolder.HeaderConfig(
- wallets = listOf(
+ wallets = persistentListOf(
WalletPreviewData.walletCardContent,
WalletPreviewData.walletCardLoading,
WalletPreviewData.walletCardHiddenContent,
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt
similarity index 83%
rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/viewmodels/WalletViewModel.kt
rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt
index c69e4f439b..a565bb8c91 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/viewmodels/WalletViewModel.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt
@@ -1,12 +1,12 @@
-package com.tangem.feature.wallet.presentation.viewmodels
+package com.tangem.feature.wallet.presentation.wallet.viewmodels
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
+import com.tangem.feature.wallet.presentation.common.WalletPreviewData
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
-import com.tangem.feature.wallet.presentation.state.WalletStateHolder
-import com.tangem.feature.wallet.presentation.ui.WalletPreviewData
+import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlin.properties.Delegates